1 /*< ilib.js */
  2 /*
  3  * ilib.js - define the ilib name space
  4  * 
  5  * Copyright © 2012-2018, JEDLSoft
  6  *
  7  * Licensed under the Apache License, Version 2.0 (the "License");
  8  * you may not use this file except in compliance with the License.
  9  * You may obtain a copy of the License at
 10  *
 11  *     http://www.apache.org/licenses/LICENSE-2.0
 12  *
 13  * Unless required by applicable law or agreed to in writing, software
 14  * distributed under the License is distributed on an "AS IS" BASIS,
 15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 16  *
 17  * See the License for the specific language governing permissions and
 18  * limitations under the License.
 19  */
 20 
 21 /**
 22  * @namespace The global namespace that contains general ilib functions useful
 23  * to all of ilib
 24  * 
 25  * @version "13.3.0"
 26  */
 27 var ilib = ilib || {};
 28 
 29 /** @private */
 30 ilib._ver = function() {
 31     return "13.3.0"
 32     ;
 33 };
 34 
 35 /**
 36  * Return the current version of ilib.
 37  * 
 38  * @static
 39  * @return {string} a version string for this instance of ilib
 40  */
 41 ilib.getVersion = function () {
 42     if (ilib._dyncode) {
 43         try {
 44             var pkg;
 45             pkg = require("../package.json");
 46             return pkg.version;
 47         } catch (e) {
 48             // ignore
 49         }
 50     }
 51     return ilib._ver() || "13.0"; 
 52 };
 53 
 54 /**
 55  * Place where resources and such are eventually assigned.
 56  */
 57 ilib.data = {
 58 	/** @type {{ccc:Object.<string,number>,nfd:Object.<string,string>,nfc:Object.<string,string>,nfkd:Object.<string,string>,nfkc:Object.<string,string>}} */
 59     norm: {
 60     	ccc: {},
 61     	nfd: {},
 62     	nfc: {},
 63     	nfkd: {},
 64     	nfkc: {}
 65     },
 66     zoneinfo: {
 67         "Etc/UTC":{"o":"0:0","f":"UTC"},
 68         "local":{"f":"local"}
 69     },
 70     /** @type {Object.<string,{to:Object.<string,string>,from:Object.<string,number>}>} */ charmaps: {},
 71     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype: null,
 72     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_c: null,
 73     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_l: null,
 74     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_m: null,
 75     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_p: null,
 76     /** @type {null|Object.<string,Array.<Array.<number>>>} */ ctype_z: null,
 77     /** @type {null|Object.<string,Array.<Array.<number>>>} */ scriptToRange: null,
 78     /** @type {null|Object.<string,string|Object.<string|Object.<string,string>>>} */ dateformats: null,
 79     /** @type {null|Array.<string>} */ timezones: [],
 80     cache: {}
 81 };
 82 
 83 /*
 84 if (typeof(window) !== 'undefined') {
 85     window["ilib"] = ilib;
 86 }
 87 */
 88 
 89 // export ilib for use as a module in nodejs
 90 if (typeof(module) !== 'undefined') {
 91     module.exports = ilib;
 92     module.exports.ilib = ilib;  // for backwards compatibility with older versions of ilib
 93 }
 94 
 95 /**
 96  * Sets the pseudo locale. Pseudolocalization (or pseudo-localization) is used for testing
 97  * internationalization aspects of software. Instead of translating the text of the software
 98  * into a foreign language, as in the process of localization, the textual elements of an application
 99  * are replaced with an altered version of the original language.These specific alterations make
100  * the original words appear readable, but include the most problematic characteristics of 
101  * the world's languages: varying length of text or characters, language direction, and so on.
102  * Regular Latin pseudo locale: eu-ES and RTL pseudo locale: ps-AF
103  * 
104  * @param {string|undefined|null} localename the locale specifier for the pseudo locale
105  */
106 ilib.setAsPseudoLocale = function (localename) {
107    if (localename) {
108 	   ilib.pseudoLocales.push(localename)
109    }
110 };
111 
112 /**
113  * Reset the list of pseudo locales back to the default single locale of zxx-XX.
114  * @static
115  */
116 ilib.clearPseudoLocales = function() {
117 	ilib.pseudoLocales = [
118         "zxx-XX",
119         "zxx-Cyrl-XX",
120         "zxx-Hans-XX",
121         "zxx-Hebr-XX"
122     ];
123 };
124 
125 ilib.clearPseudoLocales();
126 
127 /**
128  * Return the name of the platform
129  * @private
130  * @static
131  * @return {string} string naming the platform
132  */
133 ilib._getPlatform = function () {
134     if (!ilib._platform) {
135     	try {
136     		if (typeof(java.lang.Object) !== 'undefined') {
137     			ilib._platform = (typeof(process) !== 'undefined') ? "trireme" : "rhino";
138     			return ilib._platform;
139     		}
140     	} catch (e) {}
141     	
142         if (typeof(process) !== 'undefined' && process.versions && process.versions.node && typeof(module) !== 'undefined') {
143             ilib._platform = "nodejs";
144         } else if (typeof(Qt) !== 'undefined') {
145         	ilib._platform = "qt";
146         } else if (typeof(window) !== 'undefined') {
147             ilib._platform = (typeof(PalmSystem) !== 'undefined') ? "webos" : "browser";
148         } else {
149             ilib._platform = "unknown";
150         }
151     }    
152     return ilib._platform;
153 };
154 
155 /**
156  * If this ilib is running in a browser, return the name of that browser.
157  * @private
158  * @static
159  * @return {string|undefined} the name of the browser that this is running in ("firefox", "chrome", "ie", 
160  * "safari", or "opera"), or undefined if this is not running in a browser or if
161  * the browser name could not be determined 
162  */
163 ilib._getBrowser = function () {
164 	var browser = undefined;
165 	if (ilib._getPlatform() === "browser") {
166 		if (navigator && navigator.userAgent) {
167 			if (navigator.userAgent.indexOf("Firefox") > -1) {
168 				browser = "firefox";
169 			}
170 			if (navigator.userAgent.search(/Opera|OPR/) > -1 ) {
171 				browser = "opera";
172 			}
173 			if (navigator.userAgent.indexOf("Chrome") > -1) {
174 				browser = "chrome";
175 			}
176 			if (navigator.userAgent.indexOf(" .NET") > -1) {
177 				browser = "ie";
178 			}
179 			if (navigator.userAgent.indexOf("Safari") > -1) {
180 				// chrome also has the string Safari in its userAgent, but the chrome case is 
181 				// already taken care of above
182 				browser = "safari";
183 			}
184             if (navigator.userAgent.indexOf("Edge") > -1) {                
185                 browser = "Edge";
186             }
187             if (navigator.userAgent.search(/iPad|iPhone|iPod/) > -1) {
188                 // Due to constraints of the iOS platform, 
189                 // all browser must be built on top of the WebKit rendering engine
190                 browser = "iOS";
191             }
192 		}
193 	}
194 	return browser;
195 };
196 
197 /**
198  * Return the value of a global variable given its name in a way that works 
199  * correctly for the current platform.
200  * @private
201  * @static
202  * @param {string} name the name of the variable to return
203  * @return {*} the global variable, or undefined if it does not exist
204  */
205 ilib._global = function(name) {
206     switch (ilib._getPlatform()) {
207         case "rhino":
208             var top = (function() {
209               return (typeof global === 'object') ? global : this;
210             })();
211             break;
212         case "nodejs":
213         case "trireme":
214             top = typeof(global) !== 'undefined' ? global : this;
215             //console.log("ilib._global: top is " + (typeof(global) !== 'undefined' ? "global" : "this"));
216             break;
217         case "qt":
218         	return undefined;
219         default:
220         	top = window;
221         	break;
222     }
223     try {
224 		return top[name];
225 	} catch (e) {
226 		return undefined;
227 	}
228 };
229 
230 /**
231  * Return true if the global variable is defined on this platform.
232  * @private
233  * @static
234  * @param {string} name the name of the variable to check
235  * @return {boolean} true if the global variable is defined on this platform, false otherwise
236  */
237 ilib._isGlobal = function(name) {
238 	return typeof(ilib._global(name)) !== 'undefined';
239 };
240 
241 /**
242  * Clear the file load cache. This is mainly used by the unit tests,
243  * but could be used by regular callers if you want to free up memory
244  * for garbage collecting.
245  */
246 ilib.clearCache = function() {
247 	ilib.data.cache = {};
248 };
249 
250 /**
251  * Sets the default locale for all of ilib. This locale will be used
252  * when no explicit locale is passed to any ilib class. If the default
253  * locale is not set, ilib will attempt to use the locale of the
254  * environment it is running in, if it can find that. If not, it will
255  * default to the locale "en-US". If a type of parameter is string, 
256  * ilib will take only well-formed BCP-47 tag  <p>
257  * 
258  * 
259  * @static
260  * @param {string|undefined|null} spec the locale specifier for the default locale
261  */
262 ilib.setLocale = function (spec) {
263     if (typeof(spec) === 'string' || !spec) {
264         ilib.locale = spec;
265     }
266     // else ignore other data types, as we don't have the dependencies
267     // to look into them to find a locale
268 };
269 
270 /**
271  * Return the default locale for all of ilib if one has been set. This 
272  * locale will be used when no explicit locale is passed to any ilib 
273  * class. If the default
274  * locale is not set, ilib will attempt to use the locale of the
275  * environment it is running in, if it can find that. If not, it will
276  * default to the locale "en-US".<p>
277  * 
278  * 
279  * @static
280  * @return {string} the locale specifier for the default locale
281  */
282 ilib.getLocale = function () {
283     if (typeof(ilib.locale) !== 'string') {
284     	var lang, plat = ilib._getPlatform();
285     	switch (plat) {
286     		case 'browser':
287             	// running in a browser
288                 if(typeof(navigator.language) !== 'undefined') {
289                     ilib.locale = navigator.language.substring(0,3) + navigator.language.substring(3,5).toUpperCase();  // FF/Opera/Chrome/Webkit    
290                 }
291                 if (!ilib.locale) {
292                     // IE on Windows
293                     lang = typeof(navigator.browserLanguage) !== 'undefined' ? 
294                         navigator.browserLanguage :
295                         (typeof(navigator.userLanguage) !== 'undefined' ? 
296                             navigator.userLanguage :
297                             (typeof(navigator.systemLanguage) !== 'undefined' ?
298                                 navigator.systemLanguage :
299                                 undefined));
300                     if (typeof(lang) !== 'undefined' && lang) {
301                         // for some reason, MS uses lower case region tags
302                         ilib.locale = lang.substring(0,3) + lang.substring(3,5).toUpperCase();
303                     }
304                 }
305                 break;
306     		case 'webos':
307                 // webOS
308                 if (typeof(PalmSystem.locales) !== 'undefined' && 
309                 		typeof(PalmSystem.locales.UI) != 'undefined' && 
310                 		PalmSystem.locales.UI.length > 0) {
311                     ilib.locale = PalmSystem.locales.UI;
312                 } else if (typeof(PalmSystem.locale) !== 'undefined') {
313                 	ilib.locale = PalmSystem.locale;
314                 }
315     			break;
316     		case 'rhino':
317                 if (typeof(environment) !== 'undefined' && environment.user && typeof(environment.user.language) === 'string' && environment.user.language.length > 0) {
318                 	// running under plain rhino
319                     ilib.locale = environment.user.language;
320                     if (typeof(environment.user.country) === 'string' && environment.user.country.length > 0) {
321                         ilib.locale += '-' + environment.user.country;
322                     }
323                 }
324                 break;
325     		case "trireme":
326             	// under trireme on rhino emulating nodejs
327             	lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL;
328                 // the LANG variable on unix is in the form "lang_REGION.CHARSET"
329                 // where language and region are the correct ISO codes separated by
330                 // an underscore. This translate it back to the BCP-47 form.
331                 if (lang && typeof(lang) !== 'undefined') {
332                     ilib.locale = lang.substring(0,2).toLowerCase() + '-' + lang.substring(3,5).toUpperCase();
333                 }
334             	break;
335     		case 'nodejs':
336                 // running under nodejs
337                 lang = process.env.LANG || process.env.LC_ALL;
338                 // the LANG variable on unix is in the form "lang_REGION.CHARSET"
339                 // where language and region are the correct ISO codes separated by
340                 // an underscore. This translate it back to the BCP-47 form.
341                 if (lang && typeof(lang) !== 'undefined') {
342                     ilib.locale = lang.substring(0,2).toLowerCase() + '-' + lang.substring(3,5).toUpperCase();
343                 }
344     			break;
345     		case 'qt':
346             	// running in the Javascript engine under Qt/QML
347             	var locobj = Qt.locale();
348             	lang = locobj.name && locobj.name.replace("_", "-") || "en-US";
349     			break;
350     	}
351         ilib.locale = typeof(ilib.locale) === 'string' && ilib.locale ? ilib.locale : 'en-US';
352         if (ilib.locale === "en") {
353         	ilib.locale = "en-US"; // hack to get various platforms working correctly
354         }
355     }
356     return ilib.locale;
357 };
358 
359 /**
360  * Sets the default time zone for all of ilib. This time zone will be used when
361  * no explicit time zone is passed to any ilib class. If the default time zone
362  * is not set, ilib will attempt to use the time zone of the
363  * environment it is running in, if it can find that. If not, it will
364  * default to the the UTC zone "Etc/UTC".<p>
365  * 
366  * 
367  * @static
368  * @param {string} tz the name of the time zone to set as the default time zone
369  */
370 ilib.setTimeZone = function (tz) {
371     ilib.tz = tz || ilib.tz;
372 };
373 
374 /**
375  * Return the default time zone for all of ilib if one has been set. This 
376  * time zone will be used when no explicit time zone is passed to any ilib 
377  * class. If the default time zone
378  * is not set, ilib will attempt to use the locale of the
379  * environment it is running in, if it can find that. If not, it will
380  * default to the the zone "local".<p>
381  * 
382  * 
383  * @static
384  * @return {string} the default time zone for ilib
385  */
386 ilib.getTimeZone = function() {
387     if (typeof(ilib.tz) === 'undefined') {
388         if (typeof(Intl) !== 'undefined' && typeof(Intl.DateTimeFormat) !== 'undefined') {
389             var ro = new Intl.DateTimeFormat().resolvedOptions();
390             ilib.tz = ro && ro.timeZone;
391         }
392         
393         if (!ilib.tz) {
394             if (typeof(navigator) !== 'undefined' && typeof(navigator.timezone) !== 'undefined') {
395                 // running in a browser
396                 if (navigator.timezone.length > 0) {
397                     ilib.tz = navigator.timezone;
398                 }
399             } else if (typeof(PalmSystem) !== 'undefined' && typeof(PalmSystem.timezone) !== 'undefined') {
400                 // running in webkit on webOS
401                 if (PalmSystem.timezone.length > 0) {
402                     ilib.tz = PalmSystem.timezone;
403                 }
404             } else if (typeof(environment) !== 'undefined' && typeof(environment.user) !== 'undefined') {
405                 // running under rhino
406                 if (typeof(environment.user.timezone) !== 'undefined' && environment.user.timezone.length > 0) {
407                     ilib.tz = environment.user.timezone;
408                 }
409             } else if (typeof(process) !== 'undefined' && typeof(process.env) !== 'undefined') {
410                 // running in nodejs
411                 if (process.env.TZ && typeof(process.env.TZ) !== "undefined") {
412                     ilib.tz = process.env.TZ;
413                 }
414             }
415             
416             ilib.tz = ilib.tz || "local";
417         }
418     }
419 
420     return ilib.tz;
421 };
422 
423 /**
424  * @class
425  * Defines the interface for the loader class for ilib. The main method of the
426  * loader object is loadFiles(), which loads a set of requested locale data files
427  * from where-ever it is stored.
428  * @interface
429  */
430 ilib.Loader = function() {};
431 
432 /**
433  * Load a set of files from where-ever it is stored.<p>
434  * 
435  * This is the main function define a callback function for loading missing locale 
436  * data or resources.
437  * If this copy of ilib is assembled without including the required locale data
438  * or resources, then that data can be lazy loaded dynamically when it is 
439  * needed by calling this method. Each ilib class will first
440  * check for the existence of data under ilib.data, and if it is not there, 
441  * it will attempt to load it by calling this method of the laoder, and then place
442  * it there.<p>
443  * 
444  * Suggested implementations of this method might load files 
445  * directly from disk under nodejs or rhino, or within web pages, to load 
446  * files from the server with XHR calls.<p>
447  * 
448  * The first parameter to this method, paths, is an array of relative paths within 
449  * the ilib dir structure for the 
450  * requested data. These paths will already have the locale spec integrated 
451  * into them, so no further tweaking needs to happen to load the data. Simply
452  * load the named files. The second
453  * parameter tells the loader whether to load the files synchronously or asynchronously.
454  * If the sync parameters is false, then the onLoad function must also be specified.
455  * The third parameter gives extra parameters to the loader passed from the calling
456  * code. This may contain any property/value pairs.  The last parameter, callback,
457  * is a callback function to call when all of the data is finishing loading. Make
458  * sure to call the callback with the context of "this" so that the caller has their 
459  * context back again.<p>
460  * 
461  * The loader function must be able to operate either synchronously or asychronously. 
462  * If the loader function is called with an undefined callback function, it is
463  * expected to load the data synchronously, convert it to javascript
464  * objects, and return the array of json objects as the return value of the 
465  * function. If the loader 
466  * function is called with a callback function, it may load the data 
467  * synchronously or asynchronously (doesn't matter which) as long as it calls
468  * the callback function with the data converted to a javascript objects
469  * when it becomes available. If a particular file could not be loaded, the 
470  * loader function should put undefined into the corresponding entry in the
471  * results array. 
472  * Note that it is important that all the data is loaded before the callback
473  * is called.<p>
474  * 
475  * An example implementation for nodejs might be:
476  * 
477  * <pre>
478  *  * 
479  * var myLoader = function() {};
480  * myLoader.prototype = new Loader();
481  * myLoader.prototype.constructor = myLoader;
482  * myLoader.prototype.loadFiles = function(paths, sync, params, callback) {
483  *    if (sync) {
484  *        var ret = [];
485  *        // synchronous load -- just return the result
486  *        paths.forEach(function (path) {
487  *            var json = fs.readFileSync(path, "utf-8");
488  *            ret.push(json ? JSON.parse(json) : undefined);
489  *        });
490  *        
491  *        return ret;
492  *    }
493  *    this.callback = callback;
494  *
495  *    // asynchronous
496  *    this.results = [];
497  *    this._loadFilesAsync(paths);
498  * }
499  * myLoader.prototype._loadFilesAsync = function (paths) {
500  *    if (paths.length > 0) {
501  *        var file = paths.shift();
502  *        fs.readFile(file, "utf-8", function(err, json) {
503  *            this.results.push(err ? undefined : JSON.parse(json));
504  *            // call self recursively so that the callback is only called at the end
505  *            // when all the files are loaded sequentially
506  *            if (paths.length > 0) {
507  *                this._loadFilesAsync(paths);
508  *            } else {
509  *                this.callback(this.results);
510  *            }
511  *        });
512  *     }
513  * }
514  * 
515  * // bind to "this" so that "this" is relative to your own instance
516  * ilib.setLoaderCallback(new myLoader());
517  * </pre>
518 
519  * @param {Array.<string>} paths An array of paths to load from wherever the files are stored 
520  * @param {Boolean} sync if true, load the files synchronously, and false means asynchronously
521  * @param {Object} params an object with any extra parameters for the loader. These can be 
522  * anything. The caller of the ilib class passes these parameters in. Presumably, the code that
523  * calls ilib and the code that provides the loader are together and can have a private 
524  * agreement between them about what the parameters should contain.
525  * @param {function(Object)} callback function to call when the files are all loaded. The 
526  * parameter of the callback function is the contents of the files.
527  */
528 ilib.Loader.prototype.loadFiles = function (paths, sync, params, callback) {};
529 
530 /**
531  * Return all files available for loading using this loader instance.
532  * This method returns an object where the properties are the paths to
533  * directories where files are loaded from and the values are an array
534  * of strings containing the relative paths under the directory of each
535  * file that can be loaded.<p>
536  * 
537  * Example:
538  *  <pre>
539  *  {
540  *      "/usr/share/javascript/ilib/locale": [
541  *          "dateformats.json",
542  *          "aa/dateformats.json",
543  *          "af/dateformats.json",
544  *          "agq/dateformats.json",
545  *          "ak/dateformats.json",
546  *          ...
547  *          "zxx/dateformats.json"
548  *      ]
549  *  }
550  *  </pre>
551  * @returns {Object} a hash containing directory names and
552  * paths to file that can be loaded by this loader 
553  */
554 ilib.Loader.prototype.listAvailableFiles = function() {};
555 
556 /**
557  * Return true if the file in the named path is available for loading using
558  * this loader. The path may be given as an absolute path, in which case
559  * only that file is checked, or as a relative path, in which case, the
560  * relative path may appear underneath any of the directories that the loader
561  * knows about.
562  * @returns {boolean} true if the file in the named path is available for loading, and
563  * false otherwise
564  */
565 ilib.Loader.prototype.isAvailable = function(path) {};
566 
567 /**
568  * Set the custom loader used to load ilib's locale data in your environment. 
569  * The instance passed in must implement the Loader interface. See the
570  * Loader class documentation for more information about loaders. 
571  * 
572  * @static
573  * @param {ilib.Loader} loader class to call to access the requested data.
574  * @return {boolean} true if the loader was installed correctly, or false
575  * if not
576  */
577 ilib.setLoaderCallback = function(loader) {
578     // only a basic check
579     if ((typeof(loader) === 'object' && typeof(loader.loadFiles) === 'function') || 
580             typeof(loader) === 'function' || typeof(loader) === 'undefined') {
581         //console.log("setting callback loader to " + (loader ? loader.name : "undefined"));
582         ilib._load = loader;
583         return true;
584     }
585     return false;
586 };
587 
588 /**
589  * Return the custom Loader instance currently in use with this instance 
590  * of ilib. If there is no loader, this method returns undefined.
591  * 
592  * @protected
593  * @static
594  * @return {ilib.Loader|undefined} the loader instance currently in use, or 
595  * undefined if there is no such loader
596  */
597 ilib.getLoader = function() {
598 	return ilib._load;
599 };
600 
601 /**
602  * Test whether an object is an javascript array. 
603  * 
604  * @static
605  * @param {*} object The object to test
606  * @return {boolean} return true if the object is an array
607  * and false otherwise
608  */
609 ilib.isArray = function(object) {
610 	if (typeof(object) === 'object') {
611 		return Object.prototype.toString.call(object) === '[object Array]';
612 	}
613 	return false; 
614 };
615 
616 /**
617  * Extend object1 by mixing in everything from object2 into it. The objects
618  * are deeply extended, meaning that this method recursively descends the
619  * tree in the objects and mixes them in at each level. Arrays are extended
620  * by concatenating the elements of object2 onto those of object1.  
621  * 
622  * @static
623  * @param {Object} object1 the target object to extend
624  * @param {Object=} object2 the object to mix in to object1
625  * @return {Object} returns object1
626  */
627 ilib.extend = function (object1, object2) {
628 	var prop = undefined;
629 	if (object2) {
630 		for (prop in object2) {
631 			// don't extend object with undefined or functions
632 			if (prop && typeof(object2[prop]) !== 'undefined' && typeof(object2[prop]) !== "function") {
633 				if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
634 					//console.log("Merging array prop " + prop);
635 					object1[prop] = object1[prop].concat(object2[prop]);
636 				} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
637 					//console.log("Merging object prop " + prop);
638 					if (prop !== "ilib") {
639 						object1[prop] = ilib.extend(object1[prop], object2[prop]);
640 					}
641 				} else {
642 					//console.log("Copying prop " + prop);
643 					// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
644 					object1[prop] = object2[prop];
645 				}
646 			}
647 		}
648 	}
649 	return object1;
650 };
651 
652 ilib.extend2 = function (object1, object2) {
653 	var prop = undefined;
654 	if (object2) {
655 		for (prop in object2) {
656 			// don't extend object with undefined or functions
657 			if (prop && typeof(object2[prop]) !== 'undefined') {
658 				if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
659 					//console.log("Merging array prop " + prop);
660 					object1[prop] = object1[prop].concat(object2[prop]);
661 				} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
662 					//console.log("Merging object prop " + prop);
663 					if (prop !== "ilib") {
664 						object1[prop] = ilib.extend2(object1[prop], object2[prop]);
665 					}
666 				} else {
667 					//console.log("Copying prop " + prop);
668 					// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
669 					object1[prop] = object2[prop];
670 				}
671 			}
672 		}
673 	}
674 	return object1;
675 };
676 
677 /**
678  * If Function.prototype.bind does not exist in this JS engine, this
679  * function reimplements it in terms of older JS functions.
680  * bind() doesn't exist in many older browsers.
681  * 
682  * @static
683  * @param {Object} scope object that the method should operate on
684  * @param {function(...)} method method to call
685  * @return {function(...)|undefined} function that calls the given method 
686  * in the given scope with all of its arguments properly attached, or
687  * undefined if there was a problem with the arguments
688  */
689 ilib.bind = function(scope, method/*, bound arguments*/){
690 	if (!scope || !method) {
691 		return undefined;
692 	}
693 	
694 	/** @protected 
695 	 * @param {Arguments} inArrayLike
696 	 * @param {number=} inOffset
697 	 */
698 	function cloneArray(inArrayLike, inOffset) {
699 		var arr = [];
700 		for(var i = inOffset || 0, l = inArrayLike.length; i<l; i++){
701 			arr.push(inArrayLike[i]);
702 		}
703 		return arr;
704 	}
705 
706 	if (typeof(method) === 'function') {
707 		var func, args = cloneArray(arguments, 2);
708 		if (typeof(method.bind) === 'function') {
709 			func = method.bind.apply(method, [scope].concat(args));
710 		} else {
711 			func = function() {
712 				var nargs = cloneArray(arguments);
713 				// invoke with collected args
714 				return method.apply(scope, args.concat(nargs));
715 			};
716 		}
717 		return func;
718 	}
719 	return undefined;
720 };
721 
722 /**
723  * @private
724  */
725 ilib._dyncode = false;
726 
727 /**
728  * Return true if this copy of ilib is using dynamically loaded code. It returns
729  * false for pre-assembled code.
730  * 
731  * @static
732  * @return {boolean} true if this ilib uses dynamically loaded code, and false otherwise
733  */
734 ilib.isDynCode = function() {
735 	return ilib._dyncode;
736 };
737 
738 /**
739  * @private
740  */
741 ilib._dyndata = false;
742 
743 /**
744  * Return true if this copy of ilib is using dynamically loaded locale data. It returns
745  * false for pre-assembled data.
746  * 
747  * @static
748  * @return {boolean} true if this ilib uses dynamically loaded locale data, and false otherwise
749  */
750 ilib.isDynData = function() {
751 	return ilib._dyndata;
752 };
753 
754 ilib._loadtime = new Date().getTime();
755 
756 /*< JSUtils.js */
757 /*
758  * JSUtils.js - Misc utilities to work around Javascript engine differences
759  * 
760  * Copyright © 2013-2015, JEDLSoft
761  *
762  * Licensed under the Apache License, Version 2.0 (the "License");
763  * you may not use this file except in compliance with the License.
764  * You may obtain a copy of the License at
765  *
766  *     http://www.apache.org/licenses/LICENSE-2.0
767  *
768  * Unless required by applicable law or agreed to in writing, software
769  * distributed under the License is distributed on an "AS IS" BASIS,
770  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
771  *
772  * See the License for the specific language governing permissions and
773  * limitations under the License.
774  */
775 
776 // !depends ilib.js
777 
778 
779 var JSUtils = {};
780 
781 /**
782  * Perform a shallow copy of the source object to the target object. This only 
783  * copies the assignments of the source properties to the target properties, 
784  * but not recursively from there.<p>
785  * 
786  * 
787  * @static
788  * @param {Object} source the source object to copy properties from
789  * @param {Object} target the target object to copy properties into
790  */
791 JSUtils.shallowCopy = function (source, target) {
792 	var prop = undefined;
793 	if (source && target) {
794 		for (prop in source) {
795 			if (prop !== undefined && typeof(source[prop]) !== 'undefined') {
796 				target[prop] = source[prop];
797 			}
798 		}
799 	}
800 };
801 
802 /**
803  * Perform a recursive deep copy from the "from" object to the "deep" object.
804  * 
805  * @static
806  * @param {Object} from the object to copy from
807  * @param {Object} to the object to copy to
808  * @return {Object} a reference to the the "to" object
809  */
810 JSUtils.deepCopy = function(from, to) {
811 	var prop;
812 
813 	for (prop in from) {
814 		if (prop) {
815 			if (typeof(from[prop]) === 'object') {
816 				to[prop] = {};
817 				JSUtils.deepCopy(from[prop], to[prop]);
818 			} else {
819 				to[prop] = from[prop];
820 			}
821 		}
822 	}
823 	return to;
824 };
825 
826 /**
827  * Map a string to the given set of alternate characters. If the target set
828  * does not contain a particular character in the input string, then that
829  * character will be copied to the output unmapped.
830  * 
831  * @static
832  * @param {string} str a string to map to an alternate set of characters
833  * @param {Array.<string>|Object} map a mapping to alternate characters
834  * @return {string} the source string where each character is mapped to alternate characters
835  */
836 JSUtils.mapString = function (str, map) {
837 	var mapped = "";
838 	if (map && str) {
839 		for (var i = 0; i < str.length; i++) {
840 			var c = str.charAt(i); // TODO use a char iterator?
841 			mapped += map[c] || c; 
842 		}
843 	} else {
844 		mapped = str;
845 	}
846 	return mapped;
847 };
848 
849 /**
850  * Check if an object is a member of the given array. If this javascript engine
851  * support indexOf, it is used directly. Otherwise, this function implements it
852  * itself. The idea is to make sure that you can use the quick indexOf if it is
853  * available, but use a slower implementation in older engines as well.
854  * 
855  * @static
856  * @param {Array.<Object|string|number>} array array to search
857  * @param {Object|string|number} obj object being sought. This should be of the same type as the
858  * members of the array being searched. If not, this function will not return
859  * any results.
860  * @return {number} index of the object in the array, or -1 if it is not in the array.
861  */
862 JSUtils.indexOf = function(array, obj) {
863 	if (!array || !obj) {
864 		return -1;
865 	}
866 	if (typeof(array.indexOf) === 'function') {
867 		return array.indexOf(obj);
868 	} else {
869 		for (var i = 0; i < array.length; i++) {
870 	        if (array[i] === obj) {
871 	            return i;
872 	        }
873 	    }
874 	    return -1;
875 	}
876 };
877 
878 /**
879  * Pad the str with zeros to the given length of digits.
880  * 
881  * @static
882  * @param {string|number} str the string or number to pad
883  * @param {number} length the desired total length of the output string, padded
884  * @param {boolean=} right if true, pad on the right side of the number rather than the left.
885  * Default is false.
886  */
887 JSUtils.pad = function (str, length, right) {
888 	if (typeof(str) !== 'string') {
889 		str = "" + str;
890 	}
891 	var start = 0;
892 	// take care of negative numbers
893 	if (str.charAt(0) === '-') {
894 		start++;
895 	}
896 	return (str.length >= length+start) ? str : 
897 		(right ? str + JSUtils.pad.zeros.substring(0,length-str.length+start) : 
898 			str.substring(0, start) + JSUtils.pad.zeros.substring(0,length-str.length+start) + str.substring(start));
899 };
900 
901 /** @private */
902 JSUtils.pad.zeros = "00000000000000000000000000000000";
903 
904 /**
905  * Convert a string into the hexadecimal representation
906  * of the Unicode characters in that string.
907  * 
908  * @static
909  * @param {string} string The string to convert
910  * @param {number=} limit the number of digits to use to represent the character (1 to 8)
911  * @return {string} a hexadecimal representation of the
912  * Unicode characters in the input string
913  */
914 JSUtils.toHexString = function(string, limit) {
915 	var i, 
916 		result = "", 
917 		lim = (limit && limit < 9) ? limit : 4;
918 	
919 	if (!string) {
920 		return "";
921 	}
922 	for (i = 0; i < string.length; i++) {
923 		var ch = string.charCodeAt(i).toString(16);
924 		result += JSUtils.pad(ch, lim);
925 	}
926 	return result.toUpperCase();
927 };
928 
929 /**
930  * Test whether an object in a Javascript Date. 
931  * 
932  * @static
933  * @param {Object|null|undefined} object The object to test
934  * @return {boolean} return true if the object is a Date
935  * and false otherwise
936  */
937 JSUtils.isDate = function(object) {
938 	if (typeof(object) === 'object') {
939 		return Object.prototype.toString.call(object) === '[object Date]';
940 	}
941 	return false; 
942 };
943 
944 /**
945  * Merge the properties of object2 into object1 in a deep manner and return a merged
946  * object. If the property exists in both objects, the value in object2 will overwrite 
947  * the value in object1. If a property exists in object1, but not in object2, its value
948  * will not be touched. If a property exists in object2, but not in object1, it will be 
949  * added to the merged result.<p>
950  * 
951  * Name1 and name2 are for creating debug output only. They are not necessary.<p>
952  * 
953  * 
954  * @static
955  * @param {*} object1 the object to merge into
956  * @param {*} object2 the object to merge
957  * @param {boolean=} replace if true, replace the array elements in object1 with those in object2.
958  * If false, concatenate array elements in object1 with items in object2.
959  * @param {string=} name1 name of the object being merged into
960  * @param {string=} name2 name of the object being merged in
961  * @return {Object} the merged object
962  */
963 JSUtils.merge = function (object1, object2, replace, name1, name2) {
964 	var prop = undefined,
965 		newObj = {};
966 	for (prop in object1) {
967 		if (prop && typeof(object1[prop]) !== 'undefined') {
968 			newObj[prop] = object1[prop];
969 		}
970 	}
971 	for (prop in object2) {
972 		if (prop && typeof(object2[prop]) !== 'undefined') {
973 			if (ilib.isArray(object1[prop]) && ilib.isArray(object2[prop])) {
974 				if (typeof(replace) !== 'boolean' || !replace) {
975 					newObj[prop] = [].concat(object1[prop]);
976 					newObj[prop] = newObj[prop].concat(object2[prop]);
977 				} else {
978 					newObj[prop] = object2[prop];
979 				}
980 			} else if (typeof(object1[prop]) === 'object' && typeof(object2[prop]) === 'object') {
981 				newObj[prop] = JSUtils.merge(object1[prop], object2[prop], replace);
982 			} else {
983 				// for debugging. Used to determine whether or not json files are overriding their parents unnecessarily
984 				if (name1 && name2 && newObj[prop] == object2[prop]) {
985 					console.log("Property " + prop + " in " + name1 + " is being overridden by the same value in " + name2);
986 				}
987 				newObj[prop] = object2[prop];
988 			}
989 		}
990 	}
991 	return newObj;
992 };
993 
994 /**
995  * Return true if the given object has no properties.<p>
996  * 
997  * 
998  * @static
999  * @param {Object} obj the object to check
1000  * @return {boolean} true if the given object has no properties, false otherwise
1001  */
1002 JSUtils.isEmpty = function (obj) {
1003 	var prop = undefined;
1004 	
1005 	if (!obj) {
1006 		return true;
1007 	}
1008 	
1009 	for (prop in obj) {
1010 		if (prop && typeof(obj[prop]) !== 'undefined') {
1011 			return false;
1012 		}
1013 	}
1014 	return true;
1015 };
1016 
1017 /**
1018  * @static
1019  */
1020 JSUtils.hashCode = function(obj) {
1021 	var hash = 0;
1022 	
1023 	function addHash(hash, newValue) {
1024 		// co-prime numbers creates a nicely distributed hash
1025 		hash *= 65543;
1026 		hash += newValue;
1027 		hash %= 2147483647; 
1028 		return hash;
1029 	}
1030 	
1031 	function stringHash(str) {
1032 		var hash = 0;
1033 		for (var i = 0; i < str.length; i++) {
1034 			hash = addHash(hash, str.charCodeAt(i));
1035 		}
1036 		return hash;
1037 	}
1038 	
1039 	switch (typeof(obj)) {
1040 		case 'undefined':
1041 			hash = 0;
1042 			break;
1043 		case 'string':
1044 			hash = stringHash(obj);
1045 			break;
1046 		case 'function':
1047 		case 'number':
1048 		case 'xml':
1049 			hash = stringHash(String(obj));
1050 			break;
1051 		case 'boolean':
1052 			hash = obj ? 1 : 0;
1053 			break;
1054 		case 'object':
1055 			var props = [];
1056 			for (var p in obj) {
1057 				if (obj.hasOwnProperty(p)) {
1058 					props.push(p);
1059 				}
1060 			}
1061 			// make sure the order of the properties doesn't matter
1062 			props.sort();
1063 			for (var i = 0; i < props.length; i++) {
1064 				hash = addHash(hash, stringHash(props[i]));
1065 				hash = addHash(hash, JSUtils.hashCode(obj[props[i]]));
1066 			}
1067 			break;
1068 	}
1069 	
1070 	return hash;
1071 };
1072 
1073 
1074 
1075 
1076 /*< Locale.js */
1077 /*
1078  * Locale.js - Locale specifier definition
1079  * 
1080  * Copyright © 2012-2015, JEDLSoft
1081  *
1082  * Licensed under the Apache License, Version 2.0 (the "License");
1083  * you may not use this file except in compliance with the License.
1084  * You may obtain a copy of the License at
1085  *
1086  *     http://www.apache.org/licenses/LICENSE-2.0
1087  *
1088  * Unless required by applicable law or agreed to in writing, software
1089  * distributed under the License is distributed on an "AS IS" BASIS,
1090  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1091  *
1092  * See the License for the specific language governing permissions and
1093  * limitations under the License.
1094  */
1095 
1096 // !depends ilib.js JSUtils.js
1097 
1098 
1099 /**
1100  * @class
1101  * Create a new locale instance. Locales are specified either with a specifier string 
1102  * that follows the BCP-47 convention (roughly: "language-region-script-variant") or 
1103  * with 4 parameters that specify the language, region, variant, and script individually.<p>
1104  * 
1105  * The language is given as an ISO 639-1 two-letter, lower-case language code. You
1106  * can find a full list of these codes at 
1107  * <a href="http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes">http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes</a><p>
1108  * 
1109  * The region is given as an ISO 3166-1 two-letter, upper-case region code. You can
1110  * find a full list of these codes at 
1111  * <a href="http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2">http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2</a>.<p>
1112  * 
1113  * The variant is any string that does not contain a dash which further differentiates
1114  * locales from each other.<p>
1115  * 
1116  * The script is given as the ISO 15924 four-letter script code. In some locales,
1117  * text may be validly written in more than one script. For example, Serbian is often
1118  * written in both Latin and Cyrillic, though not usually mixed together. You can find a
1119  * full list of these codes at 
1120  * <a href="http://en.wikipedia.org/wiki/ISO_15924#List_of_codes">http://en.wikipedia.org/wiki/ISO_15924#List_of_codes</a>.<p>
1121  * 
1122  * As an example in ilib, the script can be used in the date formatter. Dates formatted 
1123  * in Serbian could have day-of-week names or month names written in the Latin
1124  * or Cyrillic script. Often one script is default such that sr-SR-Latn is the same
1125  * as sr-SR so the script code "Latn" can be left off of the locale spec.<p> 
1126  * 
1127  * Each part is optional, and an empty string in the specifier before or after a 
1128  * dash or as a parameter to the constructor denotes an unspecified value. In this
1129  * case, many of the ilib functions will treat the locale as generic. For example
1130  * the locale "en-" is equivalent to "en" and to "en--" and denotes a locale
1131  * of "English" with an unspecified region and variant, which typically matches
1132  * any region or variant.<p>
1133  * 
1134  * Without any arguments to the constructor, this function returns the locale of
1135  * the host Javascript engine.<p>
1136  * 
1137  * 
1138  * @constructor
1139  * @param {?string|Locale=} language the ISO 639 2-letter code for the language, or a full 
1140  * locale spec in BCP-47 format, or another Locale instance to copy from
1141  * @param {string=} region the ISO 3166 2-letter code for the region
1142  * @param {string=} variant the name of the variant of this locale, if any
1143  * @param {string=} script the ISO 15924 code of the script for this locale, if any
1144  */
1145 var Locale = function(language, region, variant, script) {
1146     if (typeof(region) === 'undefined' && typeof(variant) === 'undefined' && typeof(script) === 'undefined') {
1147 		var spec = language || ilib.getLocale();
1148 		if (typeof(spec) === 'string') {
1149 			var parts = spec.split('-');
1150 	        for ( var i = 0; i < parts.length; i++ ) {
1151 	        	if (Locale._isLanguageCode(parts[i])) {
1152 	    			/** 
1153 	    			 * @private
1154 	    			 * @type {string|undefined}
1155 	    			 */
1156 	        		this.language = parts[i];
1157 	        	} else if (Locale._isRegionCode(parts[i])) {
1158 	    			/** 
1159 	    			 * @private
1160 	    			 * @type {string|undefined}
1161 	    			 */
1162 	        		this.region = parts[i];
1163 	        	} else if (Locale._isScriptCode(parts[i])) {
1164 	    			/** 
1165 	    			 * @private
1166 	    			 * @type {string|undefined}
1167 	    			 */
1168 	        		this.script = parts[i];
1169 	        	} else {
1170 	    			/** 
1171 	    			 * @private
1172 	    			 * @type {string|undefined}
1173 	    			 */
1174 	        		this.variant = parts[i];
1175 	        	}
1176 	        }
1177 	        this.language = this.language || undefined;
1178 	        this.region = this.region || undefined;
1179 	        this.script = this.script || undefined;
1180 	        this.variant = this.variant || undefined;
1181 		} else if (typeof(spec) === 'object') {
1182 	        this.language = spec.language || undefined;
1183 	        this.region = spec.region || undefined;
1184 	        this.script = spec.script || undefined;
1185 	        this.variant = spec.variant || undefined;
1186 		}
1187 	} else {
1188 		if (language && typeof(language) === "string") {
1189 			language = language.trim();
1190 			this.language = language.length > 0 ? language.toLowerCase() : undefined;
1191 		} else {
1192 			this.language = undefined;
1193 		}
1194 		if (region && typeof(region) === "string") {
1195 			region = region.trim();
1196 			this.region = region.length > 0 ? region.toUpperCase() : undefined;
1197 		} else {
1198 			this.region = undefined;
1199 		}
1200 		if (variant && typeof(variant) === "string") {
1201 			variant = variant.trim();
1202 			this.variant = variant.length > 0 ? variant : undefined;
1203 		} else {
1204 			this.variant = undefined;
1205 		}
1206 		if (script && typeof(script) === "string") {
1207 			script = script.trim();
1208 			this.script = script.length > 0 ? script : undefined;
1209 		} else {
1210 			this.script = undefined;
1211 		}
1212 	}
1213 	this._genSpec();
1214 };
1215 
1216 // from http://en.wikipedia.org/wiki/ISO_3166-1
1217 Locale.a2toa3regmap = {
1218 	"AF": "AFG",
1219 	"AX": "ALA",
1220 	"AL": "ALB",
1221 	"DZ": "DZA",
1222 	"AS": "ASM",
1223 	"AD": "AND",
1224 	"AO": "AGO",
1225 	"AI": "AIA",
1226 	"AQ": "ATA",
1227 	"AG": "ATG",
1228 	"AR": "ARG",
1229 	"AM": "ARM",
1230 	"AW": "ABW",
1231 	"AU": "AUS",
1232 	"AT": "AUT",
1233 	"AZ": "AZE",
1234 	"BS": "BHS",
1235 	"BH": "BHR",
1236 	"BD": "BGD",
1237 	"BB": "BRB",
1238 	"BY": "BLR",
1239 	"BE": "BEL",
1240 	"BZ": "BLZ",
1241 	"BJ": "BEN",
1242 	"BM": "BMU",
1243 	"BT": "BTN",
1244 	"BO": "BOL",
1245 	"BQ": "BES",
1246 	"BA": "BIH",
1247 	"BW": "BWA",
1248 	"BV": "BVT",
1249 	"BR": "BRA",
1250 	"IO": "IOT",
1251 	"BN": "BRN",
1252 	"BG": "BGR",
1253 	"BF": "BFA",
1254 	"BI": "BDI",
1255 	"KH": "KHM",
1256 	"CM": "CMR",
1257 	"CA": "CAN",
1258 	"CV": "CPV",
1259 	"KY": "CYM",
1260 	"CF": "CAF",
1261 	"TD": "TCD",
1262 	"CL": "CHL",
1263 	"CN": "CHN",
1264 	"CX": "CXR",
1265 	"CC": "CCK",
1266 	"CO": "COL",
1267 	"KM": "COM",
1268 	"CG": "COG",
1269 	"CD": "COD",
1270 	"CK": "COK",
1271 	"CR": "CRI",
1272 	"CI": "CIV",
1273 	"HR": "HRV",
1274 	"CU": "CUB",
1275 	"CW": "CUW",
1276 	"CY": "CYP",
1277 	"CZ": "CZE",
1278 	"DK": "DNK",
1279 	"DJ": "DJI",
1280 	"DM": "DMA",
1281 	"DO": "DOM",
1282 	"EC": "ECU",
1283 	"EG": "EGY",
1284 	"SV": "SLV",
1285 	"GQ": "GNQ",
1286 	"ER": "ERI",
1287 	"EE": "EST",
1288 	"ET": "ETH",
1289 	"FK": "FLK",
1290 	"FO": "FRO",
1291 	"FJ": "FJI",
1292 	"FI": "FIN",
1293 	"FR": "FRA",
1294 	"GF": "GUF",
1295 	"PF": "PYF",
1296 	"TF": "ATF",
1297 	"GA": "GAB",
1298 	"GM": "GMB",
1299 	"GE": "GEO",
1300 	"DE": "DEU",
1301 	"GH": "GHA",
1302 	"GI": "GIB",
1303 	"GR": "GRC",
1304 	"GL": "GRL",
1305 	"GD": "GRD",
1306 	"GP": "GLP",
1307 	"GU": "GUM",
1308 	"GT": "GTM",
1309 	"GG": "GGY",
1310 	"GN": "GIN",
1311 	"GW": "GNB",
1312 	"GY": "GUY",
1313 	"HT": "HTI",
1314 	"HM": "HMD",
1315 	"VA": "VAT",
1316 	"HN": "HND",
1317 	"HK": "HKG",
1318 	"HU": "HUN",
1319 	"IS": "ISL",
1320 	"IN": "IND",
1321 	"ID": "IDN",
1322 	"IR": "IRN",
1323 	"IQ": "IRQ",
1324 	"IE": "IRL",
1325 	"IM": "IMN",
1326 	"IL": "ISR",
1327 	"IT": "ITA",
1328 	"JM": "JAM",
1329 	"JP": "JPN",
1330 	"JE": "JEY",
1331 	"JO": "JOR",
1332 	"KZ": "KAZ",
1333 	"KE": "KEN",
1334 	"KI": "KIR",
1335 	"KP": "PRK",
1336 	"KR": "KOR",
1337 	"KW": "KWT",
1338 	"KG": "KGZ",
1339 	"LA": "LAO",
1340 	"LV": "LVA",
1341 	"LB": "LBN",
1342 	"LS": "LSO",
1343 	"LR": "LBR",
1344 	"LY": "LBY",
1345 	"LI": "LIE",
1346 	"LT": "LTU",
1347 	"LU": "LUX",
1348 	"MO": "MAC",
1349 	"MK": "MKD",
1350 	"MG": "MDG",
1351 	"MW": "MWI",
1352 	"MY": "MYS",
1353 	"MV": "MDV",
1354 	"ML": "MLI",
1355 	"MT": "MLT",
1356 	"MH": "MHL",
1357 	"MQ": "MTQ",
1358 	"MR": "MRT",
1359 	"MU": "MUS",
1360 	"YT": "MYT",
1361 	"MX": "MEX",
1362 	"FM": "FSM",
1363 	"MD": "MDA",
1364 	"MC": "MCO",
1365 	"MN": "MNG",
1366 	"ME": "MNE",
1367 	"MS": "MSR",
1368 	"MA": "MAR",
1369 	"MZ": "MOZ",
1370 	"MM": "MMR",
1371 	"NA": "NAM",
1372 	"NR": "NRU",
1373 	"NP": "NPL",
1374 	"NL": "NLD",
1375 	"NC": "NCL",
1376 	"NZ": "NZL",
1377 	"NI": "NIC",
1378 	"NE": "NER",
1379 	"NG": "NGA",
1380 	"NU": "NIU",
1381 	"NF": "NFK",
1382 	"MP": "MNP",
1383 	"NO": "NOR",
1384 	"OM": "OMN",
1385 	"PK": "PAK",
1386 	"PW": "PLW",
1387 	"PS": "PSE",
1388 	"PA": "PAN",
1389 	"PG": "PNG",
1390 	"PY": "PRY",
1391 	"PE": "PER",
1392 	"PH": "PHL",
1393 	"PN": "PCN",
1394 	"PL": "POL",
1395 	"PT": "PRT",
1396 	"PR": "PRI",
1397 	"QA": "QAT",
1398 	"RE": "REU",
1399 	"RO": "ROU",
1400 	"RU": "RUS",
1401 	"RW": "RWA",
1402 	"BL": "BLM",
1403 	"SH": "SHN",
1404 	"KN": "KNA",
1405 	"LC": "LCA",
1406 	"MF": "MAF",
1407 	"PM": "SPM",
1408 	"VC": "VCT",
1409 	"WS": "WSM",
1410 	"SM": "SMR",
1411 	"ST": "STP",
1412 	"SA": "SAU",
1413 	"SN": "SEN",
1414 	"RS": "SRB",
1415 	"SC": "SYC",
1416 	"SL": "SLE",
1417 	"SG": "SGP",
1418 	"SX": "SXM",
1419 	"SK": "SVK",
1420 	"SI": "SVN",
1421 	"SB": "SLB",
1422 	"SO": "SOM",
1423 	"ZA": "ZAF",
1424 	"GS": "SGS",
1425 	"SS": "SSD",
1426 	"ES": "ESP",
1427 	"LK": "LKA",
1428 	"SD": "SDN",
1429 	"SR": "SUR",
1430 	"SJ": "SJM",
1431 	"SZ": "SWZ",
1432 	"SE": "SWE",
1433 	"CH": "CHE",
1434 	"SY": "SYR",
1435 	"TW": "TWN",
1436 	"TJ": "TJK",
1437 	"TZ": "TZA",
1438 	"TH": "THA",
1439 	"TL": "TLS",
1440 	"TG": "TGO",
1441 	"TK": "TKL",
1442 	"TO": "TON",
1443 	"TT": "TTO",
1444 	"TN": "TUN",
1445 	"TR": "TUR",
1446 	"TM": "TKM",
1447 	"TC": "TCA",
1448 	"TV": "TUV",
1449 	"UG": "UGA",
1450 	"UA": "UKR",
1451 	"AE": "ARE",
1452 	"GB": "GBR",
1453 	"US": "USA",
1454 	"UM": "UMI",
1455 	"UY": "URY",
1456 	"UZ": "UZB",
1457 	"VU": "VUT",
1458 	"VE": "VEN",
1459 	"VN": "VNM",
1460 	"VG": "VGB",
1461 	"VI": "VIR",
1462 	"WF": "WLF",
1463 	"EH": "ESH",
1464 	"YE": "YEM",
1465 	"ZM": "ZMB",
1466 	"ZW": "ZWE"
1467 };
1468 
1469 
1470 Locale.a1toa3langmap = {
1471 	"ab": "abk",
1472 	"aa": "aar",
1473 	"af": "afr",
1474 	"ak": "aka",
1475 	"sq": "sqi",
1476 	"am": "amh",
1477 	"ar": "ara",
1478 	"an": "arg",
1479 	"hy": "hye",
1480 	"as": "asm",
1481 	"av": "ava",
1482 	"ae": "ave",
1483 	"ay": "aym",
1484 	"az": "aze",
1485 	"bm": "bam",
1486 	"ba": "bak",
1487 	"eu": "eus",
1488 	"be": "bel",
1489 	"bn": "ben",
1490 	"bh": "bih",
1491 	"bi": "bis",
1492 	"bs": "bos",
1493 	"br": "bre",
1494 	"bg": "bul",
1495 	"my": "mya",
1496 	"ca": "cat",
1497 	"ch": "cha",
1498 	"ce": "che",
1499 	"ny": "nya",
1500 	"zh": "zho",
1501 	"cv": "chv",
1502 	"kw": "cor",
1503 	"co": "cos",
1504 	"cr": "cre",
1505 	"hr": "hrv",
1506 	"cs": "ces",
1507 	"da": "dan",
1508 	"dv": "div",
1509 	"nl": "nld",
1510 	"dz": "dzo",
1511 	"en": "eng",
1512 	"eo": "epo",
1513 	"et": "est",
1514 	"ee": "ewe",
1515 	"fo": "fao",
1516 	"fj": "fij",
1517 	"fi": "fin",
1518 	"fr": "fra",
1519 	"ff": "ful",
1520 	"gl": "glg",
1521 	"ka": "kat",
1522 	"de": "deu",
1523 	"el": "ell",
1524 	"gn": "grn",
1525 	"gu": "guj",
1526 	"ht": "hat",
1527 	"ha": "hau",
1528 	"he": "heb",
1529 	"hz": "her",
1530 	"hi": "hin",
1531 	"ho": "hmo",
1532 	"hu": "hun",
1533 	"ia": "ina",
1534 	"id": "ind",
1535 	"ie": "ile",
1536 	"ga": "gle",
1537 	"ig": "ibo",
1538 	"ik": "ipk",
1539 	"io": "ido",
1540 	"is": "isl",
1541 	"it": "ita",
1542 	"iu": "iku",
1543 	"ja": "jpn",
1544 	"jv": "jav",
1545 	"kl": "kal",
1546 	"kn": "kan",
1547 	"kr": "kau",
1548 	"ks": "kas",
1549 	"kk": "kaz",
1550 	"km": "khm",
1551 	"ki": "kik",
1552 	"rw": "kin",
1553 	"ky": "kir",
1554 	"kv": "kom",
1555 	"kg": "kon",
1556 	"ko": "kor",
1557 	"ku": "kur",
1558 	"kj": "kua",
1559 	"la": "lat",
1560 	"lb": "ltz",
1561 	"lg": "lug",
1562 	"li": "lim",
1563 	"ln": "lin",
1564 	"lo": "lao",
1565 	"lt": "lit",
1566 	"lu": "lub",
1567 	"lv": "lav",
1568 	"gv": "glv",
1569 	"mk": "mkd",
1570 	"mg": "mlg",
1571 	"ms": "msa",
1572 	"ml": "mal",
1573 	"mt": "mlt",
1574 	"mi": "mri",
1575 	"mr": "mar",
1576 	"mh": "mah",
1577 	"mn": "mon",
1578 	"na": "nau",
1579 	"nv": "nav",
1580 	"nb": "nob",
1581 	"nd": "nde",
1582 	"ne": "nep",
1583 	"ng": "ndo",
1584 	"nn": "nno",
1585 	"no": "nor",
1586 	"ii": "iii",
1587 	"nr": "nbl",
1588 	"oc": "oci",
1589 	"oj": "oji",
1590 	"cu": "chu",
1591 	"om": "orm",
1592 	"or": "ori",
1593 	"os": "oss",
1594 	"pa": "pan",
1595 	"pi": "pli",
1596 	"fa": "fas",
1597 	"pl": "pol",
1598 	"ps": "pus",
1599 	"pt": "por",
1600 	"qu": "que",
1601 	"rm": "roh",
1602 	"rn": "run",
1603 	"ro": "ron",
1604 	"ru": "rus",
1605 	"sa": "san",
1606 	"sc": "srd",
1607 	"sd": "snd",
1608 	"se": "sme",
1609 	"sm": "smo",
1610 	"sg": "sag",
1611 	"sr": "srp",
1612 	"gd": "gla",
1613 	"sn": "sna",
1614 	"si": "sin",
1615 	"sk": "slk",
1616 	"sl": "slv",
1617 	"so": "som",
1618 	"st": "sot",
1619 	"es": "spa",
1620 	"su": "sun",
1621 	"sw": "swa",
1622 	"ss": "ssw",
1623 	"sv": "swe",
1624 	"ta": "tam",
1625 	"te": "tel",
1626 	"tg": "tgk",
1627 	"th": "tha",
1628 	"ti": "tir",
1629 	"bo": "bod",
1630 	"tk": "tuk",
1631 	"tl": "tgl",
1632 	"tn": "tsn",
1633 	"to": "ton",
1634 	"tr": "tur",
1635 	"ts": "tso",
1636 	"tt": "tat",
1637 	"tw": "twi",
1638 	"ty": "tah",
1639 	"ug": "uig",
1640 	"uk": "ukr",
1641 	"ur": "urd",
1642 	"uz": "uzb",
1643 	"ve": "ven",
1644 	"vi": "vie",
1645 	"vo": "vol",
1646 	"wa": "wln",
1647 	"cy": "cym",
1648 	"wo": "wol",
1649 	"fy": "fry",
1650 	"xh": "xho",
1651 	"yi": "yid",
1652 	"yo": "yor",
1653 	"za": "zha",
1654 	"zu": "zul"
1655 };
1656 
1657 /**
1658  * Tell whether or not the str does not start with a lower case ASCII char.
1659  * @private
1660  * @param {string} str the char to check
1661  * @return {boolean} true if the char is not a lower case ASCII char
1662  */
1663 Locale._notLower = function(str) {
1664 	// do this with ASCII only so we don't have to depend on the CType functions
1665 	var ch = str.charCodeAt(0);
1666 	return ch < 97 || ch > 122;
1667 };
1668 
1669 /**
1670  * Tell whether or not the str does not start with an upper case ASCII char.
1671  * @private
1672  * @param {string} str the char to check
1673  * @return {boolean} true if the char is a not an upper case ASCII char
1674  */
1675 Locale._notUpper = function(str) {
1676 	// do this with ASCII only so we don't have to depend on the CType functions
1677 	var ch = str.charCodeAt(0);
1678 	return ch < 65 || ch > 90;
1679 };
1680 
1681 /**
1682  * Tell whether or not the str does not start with a digit char.
1683  * @private
1684  * @param {string} str the char to check
1685  * @return {boolean} true if the char is a not an upper case ASCII char
1686  */
1687 Locale._notDigit = function(str) {
1688 	// do this with ASCII only so we don't have to depend on the CType functions
1689 	var ch = str.charCodeAt(0);
1690 	return ch < 48 || ch > 57;
1691 };
1692 
1693 /**
1694  * Tell whether or not the given string has the correct syntax to be 
1695  * an ISO 639 language code.
1696  * 
1697  * @private
1698  * @param {string} str the string to parse
1699  * @return {boolean} true if the string could syntactically be a language code.
1700  */
1701 Locale._isLanguageCode = function(str) {
1702 	if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) {
1703 		return false;
1704 	}
1705 
1706 	for (var i = 0; i < str.length; i++) {
1707 		if (Locale._notLower(str.charAt(i))) {
1708 			return false;
1709 		}
1710 	}
1711 	
1712 	return true;
1713 };
1714 
1715 /**
1716  * Tell whether or not the given string has the correct syntax to be 
1717  * an ISO 3166 2-letter region code or M.49 3-digit region code.
1718  * 
1719  * @private
1720  * @param {string} str the string to parse
1721  * @return {boolean} true if the string could syntactically be a language code.
1722  */
1723 Locale._isRegionCode = function (str) {
1724     var i;
1725     
1726 	if (typeof(str) === 'undefined' || str.length < 2 || str.length > 3) {
1727 		return false;
1728 	}
1729 	
1730 	if (str.length === 2) {
1731 		for (i = 0; i < str.length; i++) {
1732 			if (Locale._notUpper(str.charAt(i))) {
1733 				return false;
1734 			}
1735 		}
1736 	} else {
1737 		for (i = 0; i < str.length; i++) {
1738 			if (Locale._notDigit(str.charAt(i))) {
1739 				return false;
1740 			}
1741 		}
1742 	}
1743 	
1744 	return true;
1745 };
1746 
1747 /**
1748  * Tell whether or not the given string has the correct syntax to be 
1749  * an ISO 639 language code.
1750  * 
1751  * @private
1752  * @param {string} str the string to parse
1753  * @return {boolean} true if the string could syntactically be a language code.
1754  */
1755 Locale._isScriptCode = function(str) {
1756 	if (typeof(str) === 'undefined' || str.length !== 4 || Locale._notUpper(str.charAt(0))) {
1757 		return false;
1758 	}
1759 	
1760 	for (var i = 1; i < 4; i++) {
1761 		if (Locale._notLower(str.charAt(i))) {
1762 			return false;
1763 		}
1764 	}
1765 	
1766 	return true;
1767 };
1768 
1769 /**
1770  * Return the ISO-3166 alpha3 equivalent region code for the given ISO 3166 alpha2
1771  * region code. If the given alpha2 code is not found, this function returns its
1772  * argument unchanged.
1773  * @static
1774  * @param {string|undefined} alpha2 the alpha2 code to map
1775  * @return {string|undefined} the alpha3 equivalent of the given alpha2 code, or the alpha2
1776  * parameter if the alpha2 value is not found
1777  */
1778 Locale.regionAlpha2ToAlpha3 = function(alpha2) {
1779 	return Locale.a2toa3regmap[alpha2] || alpha2;
1780 };
1781 
1782 /**
1783  * Return the ISO-639 alpha3 equivalent language code for the given ISO 639 alpha1
1784  * language code. If the given alpha1 code is not found, this function returns its
1785  * argument unchanged.
1786  * @static
1787  * @param {string|undefined} alpha1 the alpha1 code to map
1788  * @return {string|undefined} the alpha3 equivalent of the given alpha1 code, or the alpha1
1789  * parameter if the alpha1 value is not found
1790  */
1791 Locale.languageAlpha1ToAlpha3 = function(alpha1) {
1792 	return Locale.a1toa3langmap[alpha1] || alpha1;
1793 };
1794 
1795 Locale.prototype = {
1796 	/**
1797 	 * @private
1798 	 */
1799 	_genSpec: function () {
1800 		this.spec = this.language || "";
1801 		
1802 		if (this.script) {
1803 			if (this.spec.length > 0) {
1804 				this.spec += "-";
1805 			}
1806 			this.spec += this.script;
1807 		}
1808 		
1809 		if (this.region) {
1810 			if (this.spec.length > 0) {
1811 				this.spec += "-";
1812 			}
1813 			this.spec += this.region;
1814 		}
1815 		
1816 		if (this.variant) {
1817 			if (this.spec.length > 0) {
1818 				this.spec += "-";
1819 			}
1820 			this.spec += this.variant;
1821 		}
1822 	},
1823 
1824 	/**
1825 	 * Return the ISO 639 language code for this locale. 
1826 	 * @return {string|undefined} the language code for this locale 
1827 	 */
1828 	getLanguage: function() {
1829 		return this.language;
1830 	},
1831 	
1832 	/**
1833 	 * Return the language of this locale as an ISO-639-alpha3 language code
1834 	 * @return {string|undefined} the alpha3 language code of this locale
1835 	 */
1836 	getLanguageAlpha3: function() {
1837 		return Locale.languageAlpha1ToAlpha3(this.language);
1838 	},
1839 	
1840 	/**
1841 	 * Return the ISO 3166 region code for this locale.
1842 	 * @return {string|undefined} the region code of this locale
1843 	 */
1844 	getRegion: function() {
1845 		return this.region;
1846 	},
1847 	
1848 	/**
1849 	 * Return the region of this locale as an ISO-3166-alpha3 region code
1850 	 * @return {string|undefined} the alpha3 region code of this locale
1851 	 */
1852 	getRegionAlpha3: function() {
1853 		return Locale.regionAlpha2ToAlpha3(this.region);
1854 	},
1855 	
1856 	/**
1857 	 * Return the ISO 15924 script code for this locale
1858 	 * @return {string|undefined} the script code of this locale
1859 	 */
1860 	getScript: function () {
1861 		return this.script;
1862 	},
1863 	
1864 	/**
1865 	 * Return the variant code for this locale
1866 	 * @return {string|undefined} the variant code of this locale, if any
1867 	 */
1868 	getVariant: function() {
1869 		return this.variant;
1870 	},
1871 	
1872 	/**
1873 	 * Return the whole locale specifier as a string.
1874 	 * @return {string} the locale specifier
1875 	 */
1876 	getSpec: function() {
1877 	    if (!this.spec) this._genSpec();
1878 		return this.spec;
1879 	},
1880 	
1881 	/**
1882 	 * Return the language locale specifier. This includes the
1883 	 * language and the script if it is available. This can be
1884 	 * used to see whether the written language of two locales
1885 	 * match each other regardless of the region or variant.
1886 	 * 
1887 	 * @return {string} the language locale specifier
1888 	 */
1889 	getLangSpec: function() {
1890 	    var spec = this.language;
1891 	    if (spec && this.script) {
1892 	        spec += "-" + this.script;
1893 	    }
1894 	    return spec || "";
1895 	},
1896 	
1897 	/**
1898 	 * Express this locale object as a string. Currently, this simply calls the getSpec
1899 	 * function to represent the locale as its specifier.
1900 	 * 
1901 	 * @return {string} the locale specifier
1902 	 */
1903 	toString: function() {
1904 		return this.getSpec();
1905 	},
1906 	
1907 	/**
1908 	 * Return true if the the other locale is exactly equal to the current one.
1909 	 * @return {boolean} whether or not the other locale is equal to the current one 
1910 	 */
1911 	equals: function(other) {
1912 		return this.language === other.language &&
1913 			this.region === other.region &&
1914 			this.script === other.script &&
1915 			this.variant === other.variant;
1916 	},
1917 
1918 	/**
1919 	 * Return true if the current locale is the special pseudo locale.
1920 	 * @return {boolean} true if the current locale is the special pseudo locale
1921 	 */
1922 	isPseudo: function () {
1923 		return JSUtils.indexOf(ilib.pseudoLocales, this.spec) > -1;
1924 	}
1925 };
1926 
1927 // static functions
1928 /**
1929  * @private
1930  */
1931 Locale.locales = [
1932 	
1933 ];
1934 
1935 /**
1936  * Return the list of available locales that this iLib file supports.
1937  * If this copy of ilib is pre-assembled with locale data, then the 
1938  * list locales may be much smaller
1939  * than the list of all available locales in the iLib repository. The
1940  * assembly tool will automatically fill in the list for an assembled
1941  * copy of iLib. If this copy is being used with dynamically loaded 
1942  * data, then you 
1943  * can load any locale that iLib supports. You can form a locale with any 
1944  * combination of a language and region tags that exist in the locale
1945  * data directory. Language tags are in the root of the locale data dir,
1946  * and region tags can be found underneath the "und" directory. (The 
1947  * region tags are separated into a different dir because the region names 
1948  * conflict with language names on file systems that are case-insensitive.) 
1949  * If you have culled the locale data directory to limit the size of
1950  * your app, then this function should return only those files that actually exist
1951  * according to the ilibmanifest.json file in the root of that locale
1952  * data dir. Make sure your ilibmanifest.json file is up-to-date with
1953  * respect to the list of files that exist in the locale data dir.
1954  * 
1955  * @param {boolean} sync if false, load the list of available files from disk
1956  * asynchronously, otherwise load them synchronously. (Default: true/synchronously)
1957  * @param {Function} onLoad a callback function to call if asynchronous
1958  * load was requested and the list of files have been loaded.
1959  * @return {Array.<string>} this is an array of locale specs for which 
1960  * this iLib file has locale data for
1961  */
1962 Locale.getAvailableLocales = function (sync, onLoad) {
1963 	var locales = [];
1964 	if (Locale.locales.length || typeof(ilib._load.listAvailableFiles) !== 'function') {
1965 		locales = Locale.locales;
1966 		if (onLoad && typeof(onLoad) === 'function') {
1967 			onLoad(locales);
1968 		}
1969 	} else {
1970 		if (typeof(sync) === 'undefined') {
1971 			sync = true;
1972 		}
1973 		ilib._load.listAvailableFiles(sync, function(manifest) {
1974 			if (manifest) {
1975 				for (var dir in manifest) {
1976 					var filelist = manifest[dir];
1977 					for (var i = 0; i < filelist.length; i++) {
1978 						if (filelist[i].length > 15 && filelist[i].substr(-15) === "localeinfo.json") {
1979 							locales.push(filelist[i].substring(0,filelist[i].length-16).replace(/\//g, "-"));
1980 						}
1981 					}
1982 				}
1983 			}
1984 			if (onLoad && typeof(onLoad) === 'function') {
1985 				onLoad(locales);
1986 			}
1987 		});
1988 	}
1989 	return locales;
1990 };
1991 
1992 
1993 
1994 /*< Utils.js */
1995 /*
1996  * Utils.js - Core utility routines
1997  * 
1998  * Copyright © 2012-2015, JEDLSoft
1999  *
2000  * Licensed under the Apache License, Version 2.0 (the "License");
2001  * you may not use this file except in compliance with the License.
2002  * You may obtain a copy of the License at
2003  *
2004  *     http://www.apache.org/licenses/LICENSE-2.0
2005  *
2006  * Unless required by applicable law or agreed to in writing, software
2007  * distributed under the License is distributed on an "AS IS" BASIS,
2008  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2009  *
2010  * See the License for the specific language governing permissions and
2011  * limitations under the License.
2012  */
2013 
2014 // !depends ilib.js Locale.js JSUtils.js
2015 
2016 
2017 var Utils = {};
2018 
2019 /**
2020  * Find and merge all the locale data for a particular prefix in the given locale
2021  * and return it as a single javascript object. This merges the data in the 
2022  * correct order:
2023  * 
2024  * <ol>
2025  * <li>shared data (usually English)
2026  * <li>data for language
2027  * <li>data for language + region
2028  * <li>data for language + region + script
2029  * <li>data for language + region + script + variant
2030  * </ol>
2031  * 
2032  * It is okay for any of the above to be missing. This function will just skip the 
2033  * missing data. However, if everything except the shared data is missing, this 
2034  * function returns undefined, allowing the caller to go and dynamically load the
2035  * data instead.
2036  * 
2037  * @static
2038  * @param {string} prefix prefix under ilib.data of the data to merge
2039  * @param {Locale} locale locale of the data being sought
2040  * @param {boolean=} replaceArrays if true, replace the array elements in object1 with those in object2.
2041  * If false, concatenate array elements in object1 with items in object2.
2042  * @param {boolean=} returnOne if true, only return the most locale-specific data. If false,
2043  * merge all the relevant locale data together.
2044  * @return {Object?} the merged locale data
2045  */
2046 Utils.mergeLocData = function (prefix, locale, replaceArrays, returnOne) {
2047 	var data = undefined;
2048 	var loc = locale || new Locale();
2049 	var foundLocaleData = false;
2050 	var property = prefix;
2051 	var mostSpecific;
2052 
2053 	data = ilib.data[prefix] || {};
2054 
2055 	mostSpecific = data;
2056 
2057 	if (loc.getLanguage()) {
2058 		property = prefix + '_' + loc.getLanguage();
2059 		if (ilib.data[property]) {
2060 			foundLocaleData = true;
2061 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2062 			mostSpecific = ilib.data[property];
2063 		}
2064 	}
2065 	
2066 	if (loc.getRegion()) {
2067 		property = prefix + '_' + loc.getRegion();
2068 		if (ilib.data[property]) {
2069 			foundLocaleData = true;
2070 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2071 			mostSpecific = ilib.data[property];
2072 		}
2073 	}
2074 	
2075 	if (loc.getLanguage()) {
2076 		property = prefix + '_' + loc.getLanguage();
2077 		
2078 		if (loc.getScript()) {
2079 			property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript();
2080 			if (ilib.data[property]) {
2081 				foundLocaleData = true;
2082 				data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2083 				mostSpecific = ilib.data[property];
2084 			}
2085 		}
2086 		
2087 		if (loc.getRegion()) {
2088 			property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion();
2089 			if (ilib.data[property]) {
2090 				foundLocaleData = true;
2091 				data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2092 				mostSpecific = ilib.data[property];
2093 			}
2094 		}		
2095 	}
2096 	
2097 	if (loc.getRegion() && loc.getVariant()) {
2098 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getVariant();
2099 		if (ilib.data[property]) {
2100 			foundLocaleData = true;
2101 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2102 			mostSpecific = ilib.data[property];
2103 		}
2104 	}
2105 
2106 	if (loc.getLanguage() && loc.getScript() && loc.getRegion()) {
2107 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion();
2108 		if (ilib.data[property]) {
2109 			foundLocaleData = true;
2110 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2111 			mostSpecific = ilib.data[property];
2112 		}
2113 	}
2114 
2115 	if (loc.getLanguage() && loc.getRegion() && loc.getVariant()) {
2116 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getRegion() + '_' + loc.getVariant();
2117 		if (ilib.data[property]) {
2118 			foundLocaleData = true;
2119 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2120 			mostSpecific = ilib.data[property];
2121 		}
2122 	}
2123 
2124 	if (loc.getLanguage() && loc.getScript() && loc.getRegion() && loc.getVariant()) {
2125 		property = prefix + '_' + loc.getLanguage() + '_' + loc.getScript() + '_' + loc.getRegion() + '_' + loc.getVariant();
2126 		if (ilib.data[property]) {
2127 			foundLocaleData = true;
2128 			data = JSUtils.merge(data, ilib.data[property], replaceArrays);
2129 			mostSpecific = ilib.data[property];
2130 		}
2131 	}
2132 	
2133 	return foundLocaleData ? (returnOne ? mostSpecific : data) : undefined;
2134 };
2135 
2136 /**
2137  * Return an array of relative path names for the
2138  * files that represent the data for the given locale.<p>
2139  * 
2140  * Note that to prevent the situation where a directory for
2141  * a language exists next to the directory for a region where
2142  * the language code and region code differ only by case, the 
2143  * plain region directories are located under the special 
2144  * "undefined" language directory which has the ISO code "und".
2145  * The reason is that some platforms have case-insensitive 
2146  * file systems, and you cannot have 2 directories with the 
2147  * same name which only differ by case. For example, "es" is
2148  * the ISO 639 code for the language "Spanish" and "ES" is
2149  * the ISO 3166 code for the region "Spain", so both the
2150  * directories cannot exist underneath "locale". The region
2151  * therefore will be loaded from "und/ES" instead.<p>  
2152  * 
2153  * <h4>Variations</h4>
2154  * 
2155  * With only language and region specified, the following
2156  * sequence of paths will be generated:<p>
2157  * 
2158  * <pre>
2159  * language
2160  * und/region
2161  * language/region
2162  * </pre>
2163  * 
2164  * With only language and script specified:<p>
2165  * 
2166  * <pre>
2167  * language
2168  * language/script
2169  * </pre>
2170  * 
2171  * With only script and region specified:<p>
2172  * 
2173  * <pre>
2174  * und/region  
2175  * </pre>
2176  * 
2177  * With only region and variant specified:<p>
2178  * 
2179  * <pre>
2180  * und/region
2181  * region/variant
2182  * </pre>
2183  * 
2184  * With only language, script, and region specified:<p>
2185  * 
2186  * <pre>
2187  * language
2188  * und/region
2189  * language/script
2190  * language/region
2191  * language/script/region
2192  * </pre>
2193  * 
2194  * With only language, region, and variant specified:<p>
2195  * 
2196  * <pre>
2197  * language
2198  * und/region
2199  * language/region
2200  * region/variant
2201  * language/region/variant
2202  * </pre>
2203  * 
2204  * With all parts specified:<p>
2205  * 
2206  * <pre>
2207  * language
2208  * und/region
2209  * language/script
2210  * language/region
2211  * region/variant
2212  * language/script/region
2213  * language/region/variant
2214  * language/script/region/variant
2215  * </pre>
2216  * 
2217  * @static
2218  * @param {Locale} locale load the files for this locale
2219  * @param {string?} name the file name of each file to load without
2220  * any path
2221  * @return {Array.<string>} An array of relative path names
2222  * for the files that contain the locale data
2223  */
2224 Utils.getLocFiles = function(locale, name) {
2225 	var dir = "";
2226 	var files = [];
2227 	var filename = name || "resources.json";
2228 	var loc = locale || new Locale();
2229 	
2230 	var language = loc.getLanguage();
2231 	var region = loc.getRegion();
2232 	var script = loc.getScript();
2233 	var variant = loc.getVariant();
2234 	
2235 	files.push(filename); // generic shared file
2236 	
2237 	if (language) {
2238 		dir = language + "/";
2239 		files.push(dir + filename);
2240 	}
2241 	
2242 	if (region) {
2243 		dir = "und/" + region + "/";
2244 		files.push(dir + filename);
2245 	}
2246 	
2247 	if (language) {
2248 		if (script) {
2249 			dir = language + "/" + script + "/";
2250 			files.push(dir + filename);
2251 		}
2252 		if (region) {
2253 			dir = language + "/" + region + "/";
2254 			files.push(dir + filename);
2255 		}
2256 	}
2257 	
2258 	if (region && variant) {
2259 		dir = "und/" + region + "/" + variant + "/";
2260 		files.push(dir + filename);
2261 	}
2262 
2263 	if (language && script && region) {
2264 		dir = language + "/" + script + "/" + region + "/";
2265 		files.push(dir + filename);
2266 	}
2267 
2268 	if (language && region && variant) {
2269 		dir = language + "/" + region + "/" + variant + "/";
2270 		files.push(dir + filename);
2271 	}
2272 
2273 	if (language && script && region && variant) {
2274 		dir = language + "/" + script + "/" + region + "/" + variant + "/";
2275 		files.push(dir + filename);
2276 	}
2277 	
2278 	return files;
2279 };
2280 
2281 /**
2282  * Load data using the new loader object or via the old function callback.
2283  * @static
2284  * @private
2285  */
2286 Utils._callLoadData = function (files, sync, params, callback) {
2287 	// console.log("Utils._callLoadData called");
2288 	if (typeof(ilib._load) === 'function') {
2289 		// console.log("Utils._callLoadData: calling as a regular function");
2290 		return ilib._load(files, sync, params, callback);
2291 	} else if (typeof(ilib._load) === 'object' && typeof(ilib._load.loadFiles) === 'function') {
2292 		// console.log("Utils._callLoadData: calling as an object");
2293 		return ilib._load.loadFiles(files, sync, params, callback);
2294 	}
2295 	
2296 	// console.log("Utils._callLoadData: not calling. Type is " + typeof(ilib._load) + " and instanceof says " + (ilib._load instanceof Loader));
2297 	return undefined;
2298 };
2299 
2300 /**
2301  * Find locale data or load it in. If the data with the given name is preassembled, it will
2302  * find the data in ilib.data. If the data is not preassembled but there is a loader function,
2303  * this function will call it to load the data. Otherwise, the callback will be called with
2304  * undefined as the data. This function will create a cache under the given class object.
2305  * If data was successfully loaded, it will be set into the cache so that future access to 
2306  * the same data for the same locale is much quicker.<p>
2307  * 
2308  * The parameters can specify any of the following properties:<p>
2309  * 
2310  * <ul>
2311  * <li><i>name</i> - String. The name of the file being loaded. Default: ResBundle.json
2312  * <li><i>object</i> - String. The name of the class attempting to load data. This is used to differentiate parts of the cache.
2313  * <li><i>locale</i> - Locale. The locale for which data is loaded. Default is the current locale.
2314  * <li><i>nonlocale</i> - boolean. If true, the data being loaded is not locale-specific.
2315  * <li><i>type</i> - String. Type of file to load. This can be "json" or "other" type. Default: "json" 
2316  * <li><i>replace</i> - boolean. When merging json objects, this parameter controls whether to merge arrays
2317  * or have arrays replace each other. If true, arrays in child objects replace the arrays in parent 
2318  * objects. When false, the arrays in child objects are concatenated with the arrays in parent objects.  
2319  * <li><i>loadParams</i> - Object. An object with parameters to pass to the loader function
2320  * <li><i>sync</i> - boolean. Whether or not to load the data synchronously
2321  * <li><i>callback</i> - function(?)=. callback Call back function to call when the data is available.
2322  * Data is not returned from this method, so a callback function is mandatory.
2323  * </ul>
2324  * 
2325  * @static
2326  * @param {Object} params Parameters configuring how to load the files (see above)
2327  */
2328 Utils.loadData = function(params) {
2329 	var name = "resources.json",
2330 		object = "generic", 
2331 		locale = new Locale(ilib.getLocale()), 
2332 		sync = false, 
2333 		type = undefined,
2334 		loadParams = {},
2335 		callback = undefined,
2336 		nonlocale = false,
2337 		replace = false,
2338 		basename;
2339 	
2340 	if (!params || typeof(params.callback) !== 'function') {
2341 		return;
2342 	}
2343 
2344 	if (params.name) {
2345 		name = params.name;
2346 	}
2347 	if (params.object) {
2348 		object = params.object;
2349 	}
2350 	if (params.locale) {
2351 		locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
2352 	}			
2353 	if (params.type) {
2354 		type = params.type;
2355 	}
2356 	if (params.loadParams) {
2357 		loadParams = params.loadParams;
2358 	}
2359 	if (params.sync) {
2360 		sync = params.sync;
2361 	}
2362 	if (params.nonlocale) {
2363 		nonlocale = !!params.nonlocale;
2364 	}
2365 	if (typeof(params.replace) === 'boolean') {
2366 		replace = params.replace;
2367 	}
2368 	
2369 	callback = params.callback;
2370 	
2371 	if (object && !ilib.data.cache[object]) {
2372 	    ilib.data.cache[object] = {};
2373 	}
2374 	
2375 	if (!type) {
2376 		var dot = name.lastIndexOf(".");
2377 		type = (dot !== -1) ? name.substring(dot+1) : "text";
2378 	}
2379 
2380 	var spec = ((!nonlocale && locale.getSpec().replace(/-/g, '_')) || "root") + "," + name + "," + String(JSUtils.hashCode(loadParams));
2381 	if (!object || !ilib.data.cache[object] || typeof(ilib.data.cache[object][spec]) === 'undefined') {
2382 		var data, returnOne = (loadParams && loadParams.returnOne);
2383 		
2384 		if (type === "json") {
2385 			// console.log("type is json");
2386 			basename = name.substring(0, name.lastIndexOf("."));
2387 			if (nonlocale) {
2388 				basename = basename.replace(/[\.:\(\)\/\\\+\-]/g, "_");
2389 				data = ilib.data[basename];
2390 			} else {
2391 				data = Utils.mergeLocData(basename, locale, replace, returnOne);
2392 			}
2393 			if (data) {
2394 				// console.log("found assembled data");
2395 				if (object) {
2396 					ilib.data.cache[object][spec] = data;
2397 				}
2398 				callback(data);
2399 				return;
2400 			}
2401 		}
2402 		
2403 		// console.log("ilib._load is " + typeof(ilib._load));
2404 		if (typeof(ilib._load) !== 'undefined') {
2405 			// the data is not preassembled, so attempt to load it dynamically
2406 			var files = nonlocale ? [ name || "resources.json" ] : Utils.getLocFiles(locale, name);
2407 			if (type !== "json") {
2408 				loadParams.returnOne = true;
2409 			}
2410 			
2411 			Utils._callLoadData(files, sync, loadParams, ilib.bind(this, function(arr) {
2412 				if (type === "json") {
2413 					data = ilib.data[basename] || {};
2414 					for (var i = 0; i < arr.length; i++) {
2415 						if (typeof(arr[i]) !== 'undefined') {
2416 						    if (loadParams.returnOne) {
2417 						        data = arr[i];
2418 						        break;
2419 						    }
2420 							data = JSUtils.merge(data, arr[i], replace);
2421 						}
2422 					}
2423 					
2424 					if (object) {
2425 						ilib.data.cache[object][spec] = data;
2426 					}
2427 					callback(data);
2428 				} else {
2429 					var i = arr.length-1; 
2430 					while (i > -1 && !arr[i]) {
2431 						i--;
2432 					}
2433 					if (i > -1) {
2434 						if (object) {
2435 							ilib.data.cache[object][spec] = arr[i];
2436 						}
2437 						callback(arr[i]);
2438 					} else {
2439 						callback(undefined);
2440 					}
2441 				}
2442 			}));
2443 		} else {
2444 			// no data other than the generic shared data
2445 			if (type === "json") {
2446 				data = ilib.data[basename];
2447 			}
2448 			if (object && data) {
2449 				ilib.data.cache[object][spec] = data;
2450 			}
2451 			callback(data);
2452 		}
2453 	} else {
2454 		callback(ilib.data.cache && ilib.data.cache[object] && ilib.data.cache[object][spec]);
2455 	}
2456 };
2457 
2458 
2459 /*< MathUtils.js */
2460 /*
2461  * MathUtils.js - Misc math utility routines
2462  *
2463  * Copyright © 2013-2015, 2018 JEDLSoft
2464  *
2465  * Licensed under the Apache License, Version 2.0 (the "License");
2466  * you may not use this file except in compliance with the License.
2467  * You may obtain a copy of the License at
2468  *
2469  *     http://www.apache.org/licenses/LICENSE-2.0
2470  *
2471  * Unless required by applicable law or agreed to in writing, software
2472  * distributed under the License is distributed on an "AS IS" BASIS,
2473  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2474  *
2475  * See the License for the specific language governing permissions and
2476  * limitations under the License.
2477  */
2478 
2479 var MathUtils = {};
2480 
2481 /**
2482  * Return the sign of the given number. If the sign is negative, this function
2483  * returns -1. If the sign is positive or zero, this function returns 1.
2484  * @static
2485  * @param {number} num the number to test
2486  * @return {number} -1 if the number is negative, and 1 otherwise
2487  */
2488 MathUtils.signum = function (num) {
2489 	var n = num;
2490 	if (typeof(num) === 'string') {
2491 		n = parseInt(num, 10);
2492 	} else if (typeof(num) !== 'number') {
2493 		return 1;
2494 	}
2495 	return (n < 0) ? -1 : 1;
2496 };
2497 
2498 /**
2499  * @static
2500  * @protected
2501  * @param {number} num number to round
2502  * @return {number} rounded number
2503  */
2504 MathUtils.floor = function (num) {
2505 	return Math.floor(num);
2506 };
2507 
2508 /**
2509  * @static
2510  * @protected
2511  * @param {number} num number to round
2512  * @return {number} rounded number
2513  */
2514 MathUtils.ceiling = function (num) {
2515 	return Math.ceil(num);
2516 };
2517 
2518 /**
2519  * @static
2520  * @protected
2521  * @param {number} num number to round
2522  * @return {number} rounded number
2523  */
2524 MathUtils.down = function (num) {
2525 	return (num < 0) ? Math.ceil(num) : Math.floor(num);
2526 };
2527 
2528 /**
2529  * @static
2530  * @protected
2531  * @param {number} num number to round
2532  * @return {number} rounded number
2533  */
2534 MathUtils.up = function (num) {
2535 	return (num < 0) ? Math.floor(num) : Math.ceil(num);
2536 };
2537 
2538 /**
2539  * @static
2540  * @protected
2541  * @param {number} num number to round
2542  * @return {number} rounded number
2543  */
2544 MathUtils.halfup = function (num) {
2545 	return (num < 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
2546 };
2547 
2548 /**
2549  * @static
2550  * @protected
2551  * @param {number} num number to round
2552  * @return {number} rounded number
2553  */
2554 MathUtils.halfdown = function (num) {
2555 	return (num < 0) ? Math.floor(num + 0.5) : Math.ceil(num - 0.5);
2556 };
2557 
2558 /**
2559  * @static
2560  * @protected
2561  * @param {number} num number to round
2562  * @return {number} rounded number
2563  */
2564 MathUtils.halfeven = function (num) {
2565 	return (Math.floor(num) % 2 === 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
2566 };
2567 
2568 /**
2569  * @static
2570  * @protected
2571  * @param {number} num number to round
2572  * @return {number} rounded number
2573  */
2574 MathUtils.halfodd = function (num) {
2575 	return (Math.floor(num) % 2 !== 0) ? Math.ceil(num - 0.5) : Math.floor(num + 0.5);
2576 };
2577 
2578 /**
2579  * Do a proper modulo function. The Javascript % operator will give the truncated
2580  * division algorithm, but for calendrical calculations, we need the Euclidean
2581  * division algorithm where the remainder of any division, whether the dividend
2582  * is negative or not, is always a positive number in the range [0, modulus).<p>
2583  *
2584  *
2585  * @static
2586  * @param {number} dividend the number being divided
2587  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
2588  * @return the remainder of dividing the dividend by the modulus.
2589  */
2590 MathUtils.mod = function (dividend, modulus) {
2591 	if (modulus == 0) {
2592 		return 0;
2593 	}
2594 	var x = dividend % modulus;
2595 	return (x < 0) ? x + modulus : x;
2596 };
2597 
2598 /**
2599  * Do a proper adjusted modulo function. The Javascript % operator will give the truncated
2600  * division algorithm, but for calendrical calculations, we need the Euclidean
2601  * division algorithm where the remainder of any division, whether the dividend
2602  * is negative or not, is always a positive number in the range (0, modulus]. The adjusted
2603  * modulo function differs from the regular modulo function in that when the remainder is
2604  * zero, the modulus should be returned instead.<p>
2605  *
2606  *
2607  * @static
2608  * @param {number} dividend the number being divided
2609  * @param {number} modulus the number dividing the dividend. This should always be a positive number.
2610  * @return the remainder of dividing the dividend by the modulus.
2611  */
2612 MathUtils.amod = function (dividend, modulus) {
2613 	if (modulus == 0) {
2614 		return 0;
2615 	}
2616 	var x = dividend % modulus;
2617 	return (x <= 0) ? x + modulus : x;
2618 };
2619 
2620 /**
2621  * Return the number with the decimal shifted by the given precision.
2622  * Positive precisions shift the decimal to the right giving larger
2623  * numbers, and negative ones shift the decimal to the left giving
2624  * smaller numbers.
2625  *
2626  * @static
2627  * @param {number} number the number to shift
2628  * @param {number} precision the number of places to move the decimal point
2629  * @returns {number} the number with the decimal point shifted by the
2630  * given number of decimals
2631  */
2632 MathUtils.shiftDecimal = function shift(number, precision) {
2633     var numArray = ("" + number).split("e");
2634     return +(numArray[0] + "e" + (numArray[1] ? (+numArray[1] + precision) : precision));
2635 };
2636 
2637 /**
2638  * Returns the base 10 logarithm of a number. For platforms that support
2639  * Math.log10() it is used directly. For plaforms that do not, such as Qt/QML,
2640  * it will be calculated using the natural logarithm.
2641  *
2642  * @param {number} num the number to take the logarithm of
2643  * @returns {number} the base-10 logarithm of the given number
2644  */
2645 MathUtils.log10 = function(num) {
2646     if (typeof(Math.log10) === "function") {
2647         return Math.log10(num);
2648     }
2649 
2650     return Math.log(num) / Math.LN10;
2651 };
2652 
2653 /**
2654  * Return the given number with only the given number of significant digits.
2655  * The number of significant digits can start with the digits greater than
2656  * 1 and straddle the decimal point, or it may start after the decimal point.
2657  * If the number of digits requested is less than 1, the original number
2658  * will be returned unchanged.
2659  *
2660  * @static
2661  * @param {number} number the number to return with only significant digits
2662  * @param {number} digits the number of significant digits to include in the
2663  * returned number
2664  * @param {function(number): number=} round a rounding function to use
2665  * @returns {number} the given number with only the requested number of
2666  * significant digits
2667  */
2668 MathUtils.significant = function(number, digits, round) {
2669     if (digits < 1 || number === 0) return number;
2670     var rnd = round || Math.round;
2671     var factor = -Math.floor(MathUtils.log10(Math.abs(number))) + digits - 1;
2672     return MathUtils.shiftDecimal(rnd(MathUtils.shiftDecimal(number, factor)), -factor);
2673 };
2674 
2675 
2676 
2677 /*< IString.js */
2678 /*
2679  * IString.js - ilib string subclass definition
2680  *
2681  * Copyright © 2012-2015, 2018, JEDLSoft
2682  *
2683  * Licensed under the Apache License, Version 2.0 (the "License");
2684  * you may not use this file except in compliance with the License.
2685  * You may obtain a copy of the License at
2686  *
2687  *     http://www.apache.org/licenses/LICENSE-2.0
2688  *
2689  * Unless required by applicable law or agreed to in writing, software
2690  * distributed under the License is distributed on an "AS IS" BASIS,
2691  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2692  *
2693  * See the License for the specific language governing permissions and
2694  * limitations under the License.
2695  */
2696 
2697 // !depends ilib.js Utils.js Locale.js MathUtils.js
2698 
2699 // !data plurals
2700 
2701 
2702 /**
2703  * @class
2704  * Create a new ilib string instance. This string inherits from and
2705  * extends the Javascript String class. It can be
2706  * used almost anywhere that a normal Javascript string is used, though in
2707  * some instances you will need to call the {@link #toString} method when
2708  * a built-in Javascript string is needed. The formatting methods are
2709  * methods that are not in the intrinsic String class and are most useful
2710  * when localizing strings in an app or web site in combination with
2711  * the ResBundle class.<p>
2712  *
2713  * This class is named IString ("ilib string") so as not to conflict with the
2714  * built-in Javascript String class.
2715  *
2716  * @constructor
2717  * @param {string|IString=} string initialize this instance with this string
2718  */
2719 var IString = function (string) {
2720 	if (typeof(string) === 'object') {
2721 		if (string instanceof IString) {
2722 			this.str = string.str;
2723 		} else {
2724 			this.str = string.toString();
2725 		}
2726 	} else if (typeof(string) === 'string') {
2727 		this.str = new String(string);
2728 	} else {
2729 		this.str = "";
2730 	}
2731 	this.length = this.str.length;
2732 	this.cpLength = -1;
2733 	this.localeSpec = ilib.getLocale();
2734 };
2735 
2736 /**
2737  * Return true if the given character is a Unicode surrogate character,
2738  * either high or low.
2739  *
2740  * @private
2741  * @static
2742  * @param {string} ch character to check
2743  * @return {boolean} true if the character is a surrogate
2744  */
2745 IString._isSurrogate = function (ch) {
2746 	var n = ch.charCodeAt(0);
2747 	return ((n >= 0xDC00 && n <= 0xDFFF) || (n >= 0xD800 && n <= 0xDBFF));
2748 };
2749 
2750 // build in the English rule
2751 IString.plurals_default = {
2752     "one": {
2753         "and": [
2754             {
2755                 "eq": [
2756                     "i",
2757                     1
2758                 ]
2759             },
2760             {
2761                 "eq": [
2762                     "v",
2763                     0
2764                 ]
2765             }
2766         ]
2767     }
2768 };
2769 
2770 /**
2771  * Convert a UCS-4 code point to a Javascript string. The codepoint can be any valid
2772  * UCS-4 Unicode character, including supplementary characters. Standard Javascript
2773  * only supports supplementary characters using the UTF-16 encoding, which has
2774  * values in the range 0x0000-0xFFFF. String.fromCharCode() will only
2775  * give you a string containing 16-bit characters, and will not properly convert
2776  * the code point for a supplementary character (which has a value > 0xFFFF) into
2777  * two UTF-16 surrogate characters. Instead, it will just just give you whatever
2778  * single character happens to be the same as your code point modulo 0x10000, which
2779  * is almost never what you want.<p>
2780  *
2781  * Similarly, that means if you use String.charCodeAt()
2782  * you will only retrieve a 16-bit value, which may possibly be a single
2783  * surrogate character that is part of a surrogate pair representing a character
2784  * in the supplementary plane. It will not give you a code point. Use
2785  * IString.codePointAt() to access code points in a string, or use
2786  * an iterator to walk through the code points in a string.
2787  *
2788  * @static
2789  * @param {number} codepoint UCS-4 code point to convert to a character
2790  * @return {string} a string containing the character represented by the codepoint
2791  */
2792 IString.fromCodePoint = function (codepoint) {
2793 	if (codepoint < 0x10000) {
2794 		return String.fromCharCode(codepoint);
2795 	} else {
2796 		var high = Math.floor(codepoint / 0x10000) - 1;
2797 		var low = codepoint & 0xFFFF;
2798 
2799 		return String.fromCharCode(0xD800 | ((high & 0x000F) << 6) |  ((low & 0xFC00) >> 10)) +
2800 			String.fromCharCode(0xDC00 | (low & 0x3FF));
2801 	}
2802 };
2803 
2804 /**
2805  * Convert the character or the surrogate pair at the given
2806  * index into the intrinsic Javascript string to a Unicode
2807  * UCS-4 code point.
2808  *
2809  * @static
2810  * @param {string} str string to get the code point from
2811  * @param {number} index index into the string
2812  * @return {number} code point of the character at the
2813  * given index into the string
2814  */
2815 IString.toCodePoint = function(str, index) {
2816 	if (!str || str.length === 0) {
2817 		return -1;
2818 	}
2819 	var code = -1, high = str.charCodeAt(index);
2820 	if (high >= 0xD800 && high <= 0xDBFF) {
2821 		if (str.length > index+1) {
2822 			var low = str.charCodeAt(index+1);
2823 			if (low >= 0xDC00 && low <= 0xDFFF) {
2824 				code = (((high & 0x3C0) >> 6) + 1) << 16 |
2825 					(((high & 0x3F) << 10) | (low & 0x3FF));
2826 			}
2827 		}
2828 	} else {
2829 		code = high;
2830 	}
2831 
2832 	return code;
2833 };
2834 
2835 /**
2836  * Load the plural the definitions of plurals for the locale.
2837  * @param {boolean=} sync
2838  * @param {Locale|string=} locale
2839  * @param {Object=} loadParams
2840  * @param {function(*)=} onLoad
2841  */
2842 IString.loadPlurals = function (sync, locale, loadParams, onLoad) {
2843 	var loc;
2844 	if (locale) {
2845 		loc = (typeof(locale) === 'string') ? new Locale(locale) : locale;
2846 	} else {
2847 		loc = new Locale(ilib.getLocale());
2848 	}
2849 	var spec = loc.getLanguage();
2850 	if (!ilib.data["plurals_" + spec]) {
2851 		Utils.loadData({
2852 			name: "plurals.json",
2853 			object: "IString",
2854 			locale: loc,
2855 			sync: sync,
2856 			loadParams: loadParams,
2857 			callback: ilib.bind(this, function(plurals) {
2858 				if (!plurals) {
2859 					ilib.data.cache.IString[spec] = IString.plurals_default;
2860 				}
2861 				ilib.data["plurals_" + spec] = plurals || IString.plurals_default;
2862 				if (onLoad && typeof(onLoad) === 'function') {
2863 					onLoad(ilib.data["plurals_" + spec]);
2864 				}
2865 			})
2866 		});
2867 	} else {
2868 		if (onLoad && typeof(onLoad) === 'function') {
2869 			onLoad(ilib.data["plurals_" + spec]);
2870 		}
2871 	}
2872 };
2873 
2874 /**
2875  * @private
2876  * @static
2877  */
2878 IString._fncs = {
2879 	/**
2880 	 * @private
2881 	 * @param {Object} obj
2882 	 * @return {string|undefined}
2883 	 */
2884 	firstProp: function (obj) {
2885 		for (var p in obj) {
2886 			if (p && obj[p]) {
2887 				return p;
2888 			}
2889 		}
2890 		return undefined; // should never get here
2891 	},
2892 
2893 	/**
2894 	 * @private
2895 	 * @param {Object} obj
2896 	 * @return {string|undefined}
2897 	 */
2898 	firstPropRule: function (obj) {
2899 		if (Object.prototype.toString.call(obj) === '[object Array]') {
2900 			return "inrange";
2901 		} else if (Object.prototype.toString.call(obj) === '[object Object]') {
2902 			for (var p in obj) {
2903 				if (p && obj[p]) {
2904 					return p;
2905 				}
2906 			}
2907 
2908 		}
2909 		return undefined; // should never get here
2910 	},
2911 
2912 	/**
2913 	 * @private
2914 	 * @param {Object} obj
2915 	 * @param {number|Object} n
2916 	 * @return {?}
2917 	 */
2918 	getValue: function (obj, n) {
2919 		if (typeof(obj) === 'object') {
2920 			var subrule = IString._fncs.firstPropRule(obj);
2921 			if (subrule === "inrange") {
2922 				return IString._fncs[subrule](obj, n);
2923 			}
2924 			return IString._fncs[subrule](obj[subrule], n);
2925 		} else if (typeof(obj) === 'string') {
2926 			if (typeof(n) === 'object'){
2927 				return n[obj];
2928 			}
2929 			return n;
2930 		} else {
2931 			return obj;
2932 		}
2933 	},
2934 
2935 	/**
2936 	 * @private
2937 	 * @param {number|Object} n
2938 	 * @param {Array.<number|Array.<number>>|Object} range
2939 	 * @return {boolean}
2940 	 */
2941 	matchRangeContinuous: function(n, range) {
2942 
2943 		for (var num in range) {
2944 			if (typeof(num) !== 'undefined' && typeof(range[num]) !== 'undefined') {
2945 				var obj = range[num];
2946 				if (typeof(obj) === 'number') {
2947 					if (n === range[num]) {
2948 						return true;
2949 					} else if (n >= range[0] && n <= range[1]) {
2950 						return true;
2951 					}
2952 				} else if (Object.prototype.toString.call(obj) === '[object Array]') {
2953 					if (n >= obj[0] && n <= obj[1]) {
2954 						return true;
2955 					}
2956 				}
2957 			}
2958 		}
2959 		return false;
2960 	},
2961 
2962 	/**
2963 	 * @private
2964 	 * @param {*} number
2965 	 * @return {Object}
2966 	 */
2967 	calculateNumberDigits: function(number) {
2968 		var numberToString = number.toString();
2969 		var parts = [];
2970 		var numberDigits =  {};
2971 		var operandSymbol =  {};
2972 		var integerPart, decimalPartLength, decimalPart;
2973 
2974 		if (numberToString.indexOf('.') !== -1) { //decimal
2975 			parts = numberToString.split('.', 2);
2976 			numberDigits.integerPart = parseInt(parts[0], 10);
2977 			numberDigits.decimalPartLength = parts[1].length;
2978 			numberDigits.decimalPart = parseInt(parts[1], 10);
2979 
2980 			operandSymbol.n = parseFloat(number);
2981 			operandSymbol.i = numberDigits.integerPart;
2982 			operandSymbol.v = numberDigits.decimalPartLength;
2983 			operandSymbol.w = numberDigits.decimalPartLength;
2984 			operandSymbol.f = numberDigits.decimalPart;
2985 			operandSymbol.t = numberDigits.decimalPart;
2986 
2987 		} else {
2988 			numberDigits.integerPart = number;
2989 			numberDigits.decimalPartLength = 0;
2990 			numberDigits.decimalPart = 0;
2991 
2992 			operandSymbol.n = parseInt(number, 10);
2993 			operandSymbol.i = numberDigits.integerPart;
2994 			operandSymbol.v = 0;
2995 			operandSymbol.w = 0;
2996 			operandSymbol.f = 0;
2997 			operandSymbol.t = 0;
2998 
2999 		}
3000 		return operandSymbol
3001 	},
3002 
3003 	/**
3004 	 * @private
3005 	 * @param {number|Object} n
3006 	 * @param {Array.<number|Array.<number>>|Object} range
3007 	 * @return {boolean}
3008 	 */
3009 	matchRange: function(n, range) {
3010 		return IString._fncs.matchRangeContinuous(n, range);
3011 	},
3012 
3013 	/**
3014 	 * @private
3015 	 * @param {Object} rule
3016 	 * @param {number} n
3017 	 * @return {boolean}
3018 	 */
3019 	is: function(rule, n) {
3020 		var left = IString._fncs.getValue(rule[0], n);
3021 		var right = IString._fncs.getValue(rule[1], n);
3022 		return left == right;
3023 	},
3024 
3025 	/**
3026 	 * @private
3027 	 * @param {Object} rule
3028 	 * @param {number} n
3029 	 * @return {boolean}
3030 	 */
3031 	isnot: function(rule, n) {
3032 		return IString._fncs.getValue(rule[0], n) != IString._fncs.getValue(rule[1], n);
3033 	},
3034 
3035 	/**
3036 	 * @private
3037 	 * @param {Object} rule
3038 	 * @param {number|Object} n
3039 	 * @return {boolean}
3040 	 */
3041 	inrange: function(rule, n) {
3042 		if (typeof(rule[0]) === 'number') {
3043 			if(typeof(n) === 'object') {
3044 				return IString._fncs.matchRange(n.n,rule);
3045 			}
3046 			return IString._fncs.matchRange(n,rule);
3047 		} else if (typeof(rule[0]) === 'undefined') {
3048 			var subrule = IString._fncs.firstPropRule(rule);
3049 			return IString._fncs[subrule](rule[subrule], n);
3050 		} else {
3051 			return IString._fncs.matchRange(IString._fncs.getValue(rule[0], n), rule[1]);
3052 		}
3053 	},
3054 	/**
3055 	 * @private
3056 	 * @param {Object} rule
3057 	 * @param {number} n
3058 	 * @return {boolean}
3059 	 */
3060 	notin: function(rule, n) {
3061 		return !IString._fncs.matchRange(IString._fncs.getValue(rule[0], n), rule[1]);
3062 	},
3063 
3064 	/**
3065 	 * @private
3066 	 * @param {Object} rule
3067 	 * @param {number} n
3068 	 * @return {boolean}
3069 	 */
3070 	within: function(rule, n) {
3071 		return IString._fncs.matchRangeContinuous(IString._fncs.getValue(rule[0], n), rule[1]);
3072 	},
3073 
3074 	/**
3075 	 * @private
3076 	 * @param {Object} rule
3077 	 * @param {number} n
3078 	 * @return {number}
3079 	 */
3080 	mod: function(rule, n) {
3081 		return MathUtils.mod(IString._fncs.getValue(rule[0], n), IString._fncs.getValue(rule[1], n));
3082 	},
3083 
3084 	/**
3085 	 * @private
3086 	 * @param {Object} rule
3087 	 * @param {number} n
3088 	 * @return {number}
3089 	 */
3090 	n: function(rule, n) {
3091 		return n;
3092 	},
3093 
3094 	/**
3095 	 * @private
3096 	 * @param {Object} rule
3097 	 * @param {number|Object} n
3098 	 * @return {boolean}
3099 	 */
3100 	or: function(rule, n) {
3101 		var ruleLength = rule.length;
3102 		var result, i;
3103 		for (i=0; i < ruleLength; i++) {
3104 			result = IString._fncs.getValue(rule[i], n);
3105 			if (result) {
3106 				return true;
3107 			}
3108 		}
3109 		return false;
3110 	},
3111 	/**
3112 	 * @private
3113 	 * @param {Object} rule
3114 	 * @param {number|Object} n
3115 	 * @return {boolean}
3116 	 */
3117 	and: function(rule, n) {
3118 		var ruleLength = rule.length;
3119 		var result, i;
3120 		for (i=0; i < ruleLength; i++) {
3121 			result= IString._fncs.getValue(rule[i], n);
3122 			if (!result) {
3123 				return false;
3124 			}
3125 		}
3126 		return true;
3127 	},
3128 	/**
3129 	 * @private
3130 	 * @param {Object} rule
3131 	 * @param {number|Object} n
3132 	 * @return {boolean}
3133 	 */
3134 	eq: function(rule, n) {
3135 		var valueLeft = IString._fncs.getValue(rule[0], n);
3136 		var valueRight;
3137 
3138 		if (typeof(rule[0]) === 'string') {
3139 			if (typeof(n) === 'object'){
3140 				valueRight = n[rule[0]];
3141 				if (typeof(rule[1])=== 'number'){
3142 					valueRight = IString._fncs.getValue(rule[1], n);
3143 				} else if (typeof(rule[1])=== 'object' && (IString._fncs.firstPropRule(rule[1]) === "inrange" )){
3144 					valueRight = IString._fncs.getValue(rule[1], n);
3145 				}
3146 			}
3147 		} else {
3148 			if (IString._fncs.firstPropRule(rule[1]) === "inrange") { // mod
3149 				valueRight = IString._fncs.getValue(rule[1], valueLeft);
3150 			} else {
3151 				valueRight = IString._fncs.getValue(rule[1], n);
3152 			}
3153 		}
3154 		if(typeof(valueRight) === 'boolean') {
3155 			return (valueRight ? true : false);
3156 		} else {
3157 			return (valueLeft == valueRight ? true :false);
3158 		}
3159 	},
3160 	/**
3161 	 * @private
3162 	 * @param {Object} rule
3163 	 * @param {number|Object} n
3164 	 * @return {boolean}
3165 	 */
3166 	neq: function(rule, n) {
3167 		var valueLeft = IString._fncs.getValue(rule[0], n);
3168 		var valueRight;
3169 		var leftRange;
3170 		var rightRange;
3171 
3172 		if (typeof(rule[0]) === 'string') {
3173 			valueRight = n[rule[0]];
3174 			if (typeof(rule[1])=== 'number'){
3175 				valueRight = IString._fncs.getValue(rule[1], n);
3176 			} else if (typeof(rule[1]) === 'object') {
3177 				leftRange = rule[1][0];
3178 				rightRange =  rule[1][1];
3179 				if (typeof(leftRange) === 'number' &&
3180 					typeof(rightRange) === 'number'){
3181 
3182 					if (valueLeft >= leftRange && valueRight <= rightRange) {
3183 						return false
3184 					} else {
3185 						return true;
3186 					}
3187 				}
3188 			}
3189 		} else {
3190 			if (IString._fncs.firstPropRule(rule[1]) === "inrange") { // mod
3191 				valueRight = IString._fncs.getValue(rule[1], valueLeft);
3192 			} else {
3193 				valueRight = IString._fncs.getValue(rule[1], n);
3194 			}
3195 		}
3196 
3197 		if(typeof(valueRight) === 'boolean') {//mod
3198 			return (valueRight? false : true);
3199 		} else {
3200 			return (valueLeft !== valueRight ? true :false);
3201 		}
3202 
3203 	}
3204 };
3205 
3206 IString.prototype = {
3207 	/**
3208 	 * Return the length of this string in characters. This function defers to the regular
3209 	 * Javascript string class in order to perform the length function. Please note that this
3210 	 * method is a real method, whereas the length property of Javascript strings is
3211 	 * implemented by native code and appears as a property.<p>
3212 	 *
3213 	 * Example:
3214 	 *
3215 	 * <pre>
3216 	 * var str = new IString("this is a string");
3217 	 * console.log("String is " + str._length() + " characters long.");
3218 	 * </pre>
3219 	 * @private
3220 	 */
3221 	_length: function () {
3222 		return this.str.length;
3223 	},
3224 
3225 	/**
3226 	 * Format this string instance as a message, replacing the parameters with
3227 	 * the given values.<p>
3228 	 *
3229 	 * The string can contain any text that a regular Javascript string can
3230 	 * contain. Replacement parameters have the syntax:
3231 	 *
3232 	 * <pre>
3233 	 * {name}
3234 	 * </pre>
3235 	 *
3236 	 * Where "name" can be any string surrounded by curly brackets. The value of
3237 	 * "name" is taken from the parameters argument.<p>
3238 	 *
3239 	 * Example:
3240 	 *
3241 	 * <pre>
3242 	 * var str = new IString("There are {num} objects.");
3243 	 * console.log(str.format({
3244 	 *   num: 12
3245 	 * });
3246 	 * </pre>
3247 	 *
3248 	 * Would give the output:
3249 	 *
3250 	 * <pre>
3251 	 * There are 12 objects.
3252 	 * </pre>
3253 	 *
3254 	 * If a property is missing from the parameter block, the replacement
3255 	 * parameter substring is left untouched in the string, and a different
3256 	 * set of parameters may be applied a second time. This way, different
3257 	 * parts of the code may format different parts of the message that they
3258 	 * happen to know about.<p>
3259 	 *
3260 	 * Example:
3261 	 *
3262 	 * <pre>
3263 	 * var str = new IString("There are {num} objects in the {container}.");
3264 	 * console.log(str.format({
3265 	 *   num: 12
3266 	 * });
3267 	 * </pre>
3268 	 *
3269 	 * Would give the output:<p>
3270 	 *
3271 	 * <pre>
3272 	 * There are 12 objects in the {container}.
3273 	 * </pre>
3274 	 *
3275 	 * The result can then be formatted again with a different parameter block that
3276 	 * specifies a value for the container property.
3277 	 *
3278 	 * @param params a Javascript object containing values for the replacement
3279 	 * parameters in the current string
3280 	 * @return a new IString instance with as many replacement parameters filled
3281 	 * out as possible with real values.
3282 	 */
3283 	format: function (params) {
3284 		var formatted = this.str;
3285 		if (params) {
3286 			var regex;
3287 			for (var p in params) {
3288 				if (typeof(params[p]) !== 'undefined') {
3289 					regex = new RegExp("\{"+p+"\}", "g");
3290 					formatted = formatted.replace(regex, params[p]);
3291 				}
3292 			}
3293 		}
3294 		return formatted.toString();
3295 	},
3296 
3297 	/** @private */
3298     _testChoice: function(index, limit) {
3299         var numberDigits = {};
3300         var operandValue = {};
3301         
3302         switch (typeof(index)) {
3303             case 'number':
3304                 operandValue = IString._fncs.calculateNumberDigits(index);
3305 
3306                 if (limit.substring(0,2) === "<=") {
3307                     limit = parseFloat(limit.substring(2));
3308                     return operandValue.n <= limit;
3309                 } else if (limit.substring(0,2) === ">=") {
3310                     limit = parseFloat(limit.substring(2));
3311                     return operandValue.n >= limit;
3312                 } else if (limit.charAt(0) === "<") {
3313                     limit = parseFloat(limit.substring(1));
3314                     return operandValue.n < limit;
3315                 } else if (limit.charAt(0) === ">") {
3316                     limit = parseFloat(limit.substring(1));
3317                     return operandValue.n > limit;
3318                 } else {
3319                     this.locale = this.locale || new Locale(this.localeSpec);
3320                     switch (limit) {
3321                         case "zero":
3322                         case "one":
3323                         case "two":
3324                         case "few":
3325                         case "many":
3326                             // CLDR locale-dependent number classes
3327                             var ruleset = ilib.data["plurals_" + this.locale.getLanguage()];
3328                             if (ruleset) {
3329                                 var rule = ruleset[limit];
3330                                 return IString._fncs.getValue(rule, operandValue);
3331                             }
3332                             break;
3333                         case "":
3334                         case "other":
3335                             // matches anything
3336                             return true;
3337                         default:
3338                             var dash = limit.indexOf("-");
3339                             if (dash !== -1) {
3340                                 // range
3341                                 var start = limit.substring(0, dash);
3342                                 var end = limit.substring(dash+1);
3343                                 return operandValue.n >= parseInt(start, 10) && operandValue.n <= parseInt(end, 10);
3344                             } else {
3345                                 return operandValue.n === parseInt(limit, 10);
3346                             }
3347                     }
3348                 }
3349                 break;
3350             case 'boolean':
3351                 return (limit === "true" && index === true) || (limit === "false" && index === false);
3352 
3353             case 'string':
3354                 var regexp = new RegExp(limit, "i");
3355                 return regexp.test(index);
3356 
3357             case 'object':
3358                 throw "syntax error: formatChoice parameter for the argument index cannot be an object";
3359         }
3360 
3361         return false;
3362     },
3363 
3364 	/**
3365 	 * Format a string as one of a choice of strings dependent on the value of
3366 	 * a particular argument index or array of indices.<p>
3367 	 *
3368 	 * The syntax of the choice string is as follows. The string contains a
3369 	 * series of choices separated by a vertical bar character "|". Each choice
3370 	 * has a value or range of values to match followed by a hash character "#"
3371 	 * followed by the string to use if the variable matches the criteria.<p>
3372 	 *
3373 	 * Example string:
3374 	 *
3375 	 * <pre>
3376 	 * var num = 2;
3377 	 * var str = new IString("0#There are no objects.|1#There is one object.|2#There are {number} objects.");
3378 	 * console.log(str.formatChoice(num, {
3379 	 *   number: num
3380 	 * }));
3381 	 * </pre>
3382 	 *
3383 	 * Gives the output:
3384 	 *
3385 	 * <pre>
3386 	 * "There are 2 objects."
3387 	 * </pre>
3388 	 *
3389 	 * The strings to format may contain replacement variables that will be formatted
3390 	 * using the format() method above and the params argument as a source of values
3391 	 * to use while formatting those variables.<p>
3392 	 *
3393 	 * If the criterion for a particular choice is empty, that choice will be used
3394 	 * as the default one for use when none of the other choice's criteria match.<p>
3395 	 *
3396 	 * Example string:
3397 	 *
3398 	 * <pre>
3399 	 * var num = 22;
3400 	 * var str = new IString("0#There are no objects.|1#There is one object.|#There are {number} objects.");
3401 	 * console.log(str.formatChoice(num, {
3402 	 *   number: num
3403 	 * }));
3404 	 * </pre>
3405 	 *
3406 	 * Gives the output:
3407 	 *
3408 	 * <pre>
3409 	 * "There are 22 objects."
3410 	 * </pre>
3411 	 *
3412 	 * If multiple choice patterns can match a given argument index, the first one
3413 	 * encountered in the string will be used. If no choice patterns match the
3414 	 * argument index, then the default choice will be used. If there is no default
3415 	 * choice defined, then this method will return an empty string.<p>
3416 	 *
3417 	 * <b>Special Syntax</b><p>
3418 	 *
3419 	 * For any choice format string, all of the patterns in the string should be
3420 	 * of a single type: numeric, boolean, or string/regexp. The type of the
3421 	 * patterns is determined by the type of the argument index parameter.<p>
3422 	 *
3423 	 * If the argument index is numeric, then some special syntax can be used
3424 	 * in the patterns to match numeric ranges.<p>
3425 	 *
3426 	 * <ul>
3427 	 * <li><i>>x</i> - match any number that is greater than x
3428 	 * <li><i>>=x</i> - match any number that is greater than or equal to x
3429 	 * <li><i><x</i> - match any number that is less than x
3430 	 * <li><i><=x</i> - match any number that is less than or equal to x
3431 	 * <li><i>start-end</i> - match any number in the range [start,end)
3432 	 * <li><i>zero</i> - match any number in the class "zero". (See below for
3433 	 * a description of number classes.)
3434 	 * <li><i>one</i> - match any number in the class "one"
3435 	 * <li><i>two</i> - match any number in the class "two"
3436 	 * <li><i>few</i> - match any number in the class "few"
3437 	 * <li><i>many</i> - match any number in the class "many"
3438      * <li><i>other</i> - match any number in the other or default class
3439 	 * </ul>
3440 	 *
3441 	 * A number class defines a set of numbers that receive a particular syntax
3442 	 * in the strings. For example, in Slovenian, integers ending in the digit
3443 	 * "1" are in the "one" class, including 1, 21, 31, ... 101, 111, etc.
3444 	 * Similarly, integers ending in the digit "2" are in the "two" class.
3445 	 * Integers ending in the digits "3" or "4" are in the "few" class, and
3446 	 * every other integer is handled by the default string.<p>
3447 	 *
3448 	 * The definition of what numbers are included in a class is locale-dependent.
3449 	 * They are defined in the data file plurals.json. If your string is in a
3450 	 * different locale than the default for ilib, you should call the setLocale()
3451 	 * method of the string instance before calling this method.<p>
3452 	 *
3453 	 * <b>Other Pattern Types</b><p>
3454 	 *
3455 	 * If the argument index is a boolean, the string values "true" and "false"
3456 	 * may appear as the choice patterns.<p>
3457 	 *
3458 	 * If the argument index is of type string, then the choice patterns may contain
3459 	 * regular expressions, or static strings as degenerate regexps.<p>
3460 	 *
3461 	 * <b>Multiple Indexes</b><p>
3462 	 *
3463 	 * If you have 2 or more indexes to format into a string, you can pass them as
3464 	 * an array. When you do that, the patterns to match should be a comma-separate
3465 	 * list of patterns as per the rules above.<p>
3466 	 *
3467      * Example string:
3468      *
3469      * <pre>
3470      * var str = new IString("zero,zero#There are no objects on zero pages.|one,one#There is 1 object on 1 page.|other,one#There are {number} objects on 1 page.|#There are {number} objects on {pages} pages.");
3471      * var num = 4, pages = 1;
3472      * console.log(str.formatChoice([num, pages], {
3473      *   number: num,
3474      *   pages: pages
3475      * }));
3476      * </pre>
3477      *
3478      * Gives the output:<p>
3479      *
3480      * <pre>
3481      * "There are 4 objects on 1 page."
3482      * </pre>
3483      *
3484      * Note that when there is a single index, you would typically leave the pattern blank to
3485      * indicate the default choice. When there are multiple indices, sometimes one of the
3486      * patterns has to be the default case when the other is not. Rather than leaving one or
3487      * more of the patterns blank with commas that look out-of-place in the middle of it, you
3488      * can use the word "other" to indicate a match with the default or other choice. The above example
3489      * shows the use of the "other" pattern. That said, you are allowed to leave the pattern
3490      * blank if you so choose. In the example above, the pattern for the third string could
3491      * easily have been written as ",one" instead of "other,one" and the result will be the same.
3492      *
3493 	 * @param {*|Array.<*>} argIndex The index into the choice array of the current parameter,
3494 	 * or an array of indices
3495 	 * @param {Object} params The hash of parameter values that replace the replacement
3496 	 * variables in the string
3497 	 * @throws "syntax error in choice format pattern: " if there is a syntax error
3498 	 * @return {string} the formatted string
3499 	 */
3500 	formatChoice: function(argIndex, params) {
3501 		var choices = this.str.split("|");
3502 		var limits = [];
3503 		var strings = [];
3504 		var i;
3505 		var parts;
3506 		var arg;
3507 		var result = undefined;
3508 		var defaultCase = "";
3509 
3510 		if (this.str.length === 0) {
3511 			// nothing to do
3512 			return "";
3513 		}
3514 
3515 		// first parse all the choices
3516 		for (i = 0; i < choices.length; i++) {
3517 			parts = choices[i].split("#");
3518 			if (parts.length > 2) {
3519 				limits[i] = parts[0];
3520 				parts = parts.shift();
3521 				strings[i] = parts.join("#");
3522 			} else if (parts.length === 2) {
3523 				limits[i] = parts[0];
3524 				strings[i] = parts[1];
3525 			} else {
3526 				// syntax error
3527 				throw "syntax error in choice format pattern: " + choices[i];
3528 			}
3529 		}
3530 
3531 		var args = (ilib.isArray(argIndex)) ? argIndex : [argIndex];
3532 
3533 		// then apply the argument index (or indices)
3534         for (i = 0; i < limits.length; i++) {
3535             if (limits[i].length === 0) {
3536                 // this is default case
3537                 defaultCase = new IString(strings[i]);
3538             } else {
3539                 var limitsArr = (limits[i].indexOf(",") > -1) ? limits[i].split(",") : [limits[i]];
3540 
3541                 var applicable = true;
3542                 for (var j = 0; applicable && j < args.length && j < limitsArr.length; j++) {
3543                     applicable = this._testChoice(args[j], limitsArr[j]);
3544                 }
3545 
3546                 if (applicable) {
3547                     result = new IString(strings[i]);
3548                     i = limits.length;
3549                 }
3550             }
3551         }
3552 
3553         if (!result) {
3554             result = defaultCase || new IString("");
3555         }
3556 
3557 		result = result.format(params);
3558 
3559 		return result.toString();
3560 	},
3561 
3562 	// delegates
3563 	/**
3564 	 * Same as String.toString()
3565 	 * @return {string} this instance as regular Javascript string
3566 	 */
3567 	toString: function () {
3568 		return this.str.toString();
3569 	},
3570 
3571 	/**
3572 	 * Same as String.valueOf()
3573 	 * @return {string} this instance as a regular Javascript string
3574 	 */
3575 	valueOf: function () {
3576 		return this.str.valueOf();
3577 	},
3578 
3579 	/**
3580 	 * Same as String.charAt()
3581 	 * @param {number} index the index of the character being sought
3582 	 * @return {IString} the character at the given index
3583 	 */
3584 	charAt: function(index) {
3585 		return new IString(this.str.charAt(index));
3586 	},
3587 
3588 	/**
3589 	 * Same as String.charCodeAt(). This only reports on
3590 	 * 2-byte UCS-2 Unicode values, and does not take into
3591 	 * account supplementary characters encoded in UTF-16.
3592 	 * If you would like to take account of those characters,
3593 	 * use codePointAt() instead.
3594 	 * @param {number} index the index of the character being sought
3595 	 * @return {number} the character code of the character at the
3596 	 * given index in the string
3597 	 */
3598 	charCodeAt: function(index) {
3599 		return this.str.charCodeAt(index);
3600 	},
3601 
3602 	/**
3603 	 * Same as String.concat()
3604 	 * @param {string} strings strings to concatenate to the current one
3605 	 * @return {IString} a concatenation of the given strings
3606 	 */
3607 	concat: function(strings) {
3608 		return new IString(this.str.concat(strings));
3609 	},
3610 
3611 	/**
3612 	 * Same as String.indexOf()
3613 	 * @param {string} searchValue string to search for
3614 	 * @param {number} start index into the string to start searching, or
3615 	 * undefined to search the entire string
3616 	 * @return {number} index into the string of the string being sought,
3617 	 * or -1 if the string is not found
3618 	 */
3619 	indexOf: function(searchValue, start) {
3620 		return this.str.indexOf(searchValue, start);
3621 	},
3622 
3623 	/**
3624 	 * Same as String.lastIndexOf()
3625 	 * @param {string} searchValue string to search for
3626 	 * @param {number} start index into the string to start searching, or
3627 	 * undefined to search the entire string
3628 	 * @return {number} index into the string of the string being sought,
3629 	 * or -1 if the string is not found
3630 	 */
3631 	lastIndexOf: function(searchValue, start) {
3632 		return this.str.lastIndexOf(searchValue, start);
3633 	},
3634 
3635 	/**
3636 	 * Same as String.match()
3637 	 * @param {string} regexp the regular expression to match
3638 	 * @return {Array.<string>} an array of matches
3639 	 */
3640 	match: function(regexp) {
3641 		return this.str.match(regexp);
3642 	},
3643 
3644 	/**
3645 	 * Same as String.replace()
3646 	 * @param {string} searchValue a regular expression to search for
3647 	 * @param {string} newValue the string to replace the matches with
3648 	 * @return {IString} a new string with all the matches replaced
3649 	 * with the new value
3650 	 */
3651 	replace: function(searchValue, newValue) {
3652 		return new IString(this.str.replace(searchValue, newValue));
3653 	},
3654 
3655 	/**
3656 	 * Same as String.search()
3657 	 * @param {string} regexp the regular expression to search for
3658 	 * @return {number} position of the match, or -1 for no match
3659 	 */
3660 	search: function(regexp) {
3661 		return this.str.search(regexp);
3662 	},
3663 
3664 	/**
3665 	 * Same as String.slice()
3666 	 * @param {number} start first character to include in the string
3667 	 * @param {number} end include all characters up to, but not including
3668 	 * the end character
3669 	 * @return {IString} a slice of the current string
3670 	 */
3671 	slice: function(start, end) {
3672 		return new IString(this.str.slice(start, end));
3673 	},
3674 
3675 	/**
3676 	 * Same as String.split()
3677 	 * @param {string} separator regular expression to match to find
3678 	 * separations between the parts of the text
3679 	 * @param {number} limit maximum number of items in the final
3680 	 * output array. Any items beyond that limit will be ignored.
3681 	 * @return {Array.<string>} the parts of the current string split
3682 	 * by the separator
3683 	 */
3684 	split: function(separator, limit) {
3685 		return this.str.split(separator, limit);
3686 	},
3687 
3688 	/**
3689 	 * Same as String.substr()
3690 	 * @param {number} start the index of the character that should
3691 	 * begin the returned substring
3692 	 * @param {number} length the number of characters to return after
3693 	 * the start character.
3694 	 * @return {IString} the requested substring
3695 	 */
3696 	substr: function(start, length) {
3697 		var plat = ilib._getPlatform();
3698 		if (plat === "qt" || plat === "rhino" || plat === "trireme") {
3699 			// qt and rhino have a broken implementation of substr(), so
3700 			// work around it
3701 			if (typeof(length) === "undefined") {
3702 				length = this.str.length - start;
3703 			}
3704 		}
3705 		return new IString(this.str.substr(start, length));
3706 	},
3707 
3708 	/**
3709 	 * Same as String.substring()
3710 	 * @param {number} from the index of the character that should
3711 	 * begin the returned substring
3712 	 * @param {number} to the index where to stop the extraction. If
3713 	 * omitted, extracts the rest of the string
3714 	 * @return {IString} the requested substring
3715 	 */
3716 	substring: function(from, to) {
3717 		return this.str.substring(from, to);
3718 	},
3719 
3720 	/**
3721 	 * Same as String.toLowerCase(). Note that this method is
3722 	 * not locale-sensitive.
3723 	 * @return {IString} a string with the first character
3724 	 * lower-cased
3725 	 */
3726 	toLowerCase: function() {
3727 		return this.str.toLowerCase();
3728 	},
3729 
3730 	/**
3731 	 * Same as String.toUpperCase(). Note that this method is
3732 	 * not locale-sensitive. Use toLocaleUpperCase() instead
3733 	 * to get locale-sensitive behaviour.
3734 	 * @return {IString} a string with the first character
3735 	 * upper-cased
3736 	 */
3737 	toUpperCase: function() {
3738 		return this.str.toUpperCase();
3739 	},
3740 
3741 	/**
3742 	 * Convert the character or the surrogate pair at the given
3743 	 * index into the string to a Unicode UCS-4 code point.
3744 	 * @protected
3745 	 * @param {number} index index into the string
3746 	 * @return {number} code point of the character at the
3747 	 * given index into the string
3748 	 */
3749 	_toCodePoint: function (index) {
3750 		return IString.toCodePoint(this.str, index);
3751 	},
3752 
3753 	/**
3754 	 * Call the callback with each character in the string one at
3755 	 * a time, taking care to step through the surrogate pairs in
3756 	 * the UTF-16 encoding properly.<p>
3757 	 *
3758 	 * The standard Javascript String's charAt() method only
3759 	 * returns a particular 16-bit character in the
3760 	 * UTF-16 encoding scheme.
3761 	 * If the index to charAt() is pointing to a low- or
3762 	 * high-surrogate character,
3763 	 * it will return the surrogate character rather
3764 	 * than the the character
3765 	 * in the supplementary planes that the two surrogates together
3766 	 * encode. This function will call the callback with the full
3767 	 * character, making sure to join two
3768 	 * surrogates into one character in the supplementary planes
3769 	 * where necessary.<p>
3770 	 *
3771 	 * @param {function(string)} callback a callback function to call with each
3772 	 * full character in the current string
3773 	 */
3774 	forEach: function(callback) {
3775 		if (typeof(callback) === 'function') {
3776 			var it = this.charIterator();
3777 			while (it.hasNext()) {
3778 				callback(it.next());
3779 			}
3780 		}
3781 	},
3782 
3783 	/**
3784 	 * Call the callback with each numeric code point in the string one at
3785 	 * a time, taking care to step through the surrogate pairs in
3786 	 * the UTF-16 encoding properly.<p>
3787 	 *
3788 	 * The standard Javascript String's charCodeAt() method only
3789 	 * returns information about a particular 16-bit character in the
3790 	 * UTF-16 encoding scheme.
3791 	 * If the index to charCodeAt() is pointing to a low- or
3792 	 * high-surrogate character,
3793 	 * it will return the code point of the surrogate character rather
3794 	 * than the code point of the character
3795 	 * in the supplementary planes that the two surrogates together
3796 	 * encode. This function will call the callback with the full
3797 	 * code point of each character, making sure to join two
3798 	 * surrogates into one code point in the supplementary planes.<p>
3799 	 *
3800 	 * @param {function(string)} callback a callback function to call with each
3801 	 * code point in the current string
3802 	 */
3803 	forEachCodePoint: function(callback) {
3804 		if (typeof(callback) === 'function') {
3805 			var it = this.iterator();
3806 			while (it.hasNext()) {
3807 				callback(it.next());
3808 			}
3809 		}
3810 	},
3811 
3812 	/**
3813 	 * Return an iterator that will step through all of the characters
3814 	 * in the string one at a time and return their code points, taking
3815 	 * care to step through the surrogate pairs in UTF-16 encoding
3816 	 * properly.<p>
3817 	 *
3818 	 * The standard Javascript String's charCodeAt() method only
3819 	 * returns information about a particular 16-bit character in the
3820 	 * UTF-16 encoding scheme.
3821 	 * If the index is pointing to a low- or high-surrogate character,
3822 	 * it will return a code point of the surrogate character rather
3823 	 * than the code point of the character
3824 	 * in the supplementary planes that the two surrogates together
3825 	 * encode.<p>
3826 	 *
3827 	 * The iterator instance returned has two methods, hasNext() which
3828 	 * returns true if the iterator has more code points to iterate through,
3829 	 * and next() which returns the next code point as a number.<p>
3830 	 *
3831 	 * @return {Object} an iterator
3832 	 * that iterates through all the code points in the string
3833 	 */
3834 	iterator: function() {
3835 		/**
3836 		 * @constructor
3837 		 */
3838 		function _iterator (istring) {
3839 			this.index = 0;
3840 			this.hasNext = function () {
3841 				return (this.index < istring.str.length);
3842 			};
3843 			this.next = function () {
3844 				if (this.index < istring.str.length) {
3845 					var num = istring._toCodePoint(this.index);
3846 					this.index += ((num > 0xFFFF) ? 2 : 1);
3847 				} else {
3848 					num = -1;
3849 				}
3850 				return num;
3851 			};
3852 		};
3853 		return new _iterator(this);
3854 	},
3855 
3856 	/**
3857 	 * Return an iterator that will step through all of the characters
3858 	 * in the string one at a time, taking
3859 	 * care to step through the surrogate pairs in UTF-16 encoding
3860 	 * properly.<p>
3861 	 *
3862 	 * The standard Javascript String's charAt() method only
3863 	 * returns information about a particular 16-bit character in the
3864 	 * UTF-16 encoding scheme.
3865 	 * If the index is pointing to a low- or high-surrogate character,
3866 	 * it will return that surrogate character rather
3867 	 * than the surrogate pair which represents a character
3868 	 * in the supplementary planes.<p>
3869 	 *
3870 	 * The iterator instance returned has two methods, hasNext() which
3871 	 * returns true if the iterator has more characters to iterate through,
3872 	 * and next() which returns the next character.<p>
3873 	 *
3874 	 * @return {Object} an iterator
3875 	 * that iterates through all the characters in the string
3876 	 */
3877 	charIterator: function() {
3878 		/**
3879 		 * @constructor
3880 		 */
3881 		function _chiterator (istring) {
3882 			this.index = 0;
3883 			this.hasNext = function () {
3884 				return (this.index < istring.str.length);
3885 			};
3886 			this.next = function () {
3887 				var ch;
3888 				if (this.index < istring.str.length) {
3889 					ch = istring.str.charAt(this.index);
3890 					if (IString._isSurrogate(ch) &&
3891 							this.index+1 < istring.str.length &&
3892 							IString._isSurrogate(istring.str.charAt(this.index+1))) {
3893 						this.index++;
3894 						ch += istring.str.charAt(this.index);
3895 					}
3896 					this.index++;
3897 				}
3898 				return ch;
3899 			};
3900 		};
3901 		return new _chiterator(this);
3902 	},
3903 
3904 	/**
3905 	 * Return the code point at the given index when the string is viewed
3906 	 * as an array of code points. If the index is beyond the end of the
3907 	 * array of code points or if the index is negative, -1 is returned.
3908 	 * @param {number} index index of the code point
3909 	 * @return {number} code point of the character at the given index into
3910 	 * the string
3911 	 */
3912 	codePointAt: function (index) {
3913 		if (index < 0) {
3914 			return -1;
3915 		}
3916 		var count,
3917 			it = this.iterator(),
3918 			ch;
3919 		for (count = index; count >= 0 && it.hasNext(); count--) {
3920 			ch = it.next();
3921 		}
3922 		return (count < 0) ? ch : -1;
3923 	},
3924 
3925 	/**
3926 	 * Set the locale to use when processing choice formats. The locale
3927 	 * affects how number classes are interpretted. In some cultures,
3928 	 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and
3929 	 * in yet others, "few" maps to "any integer that ends in the digits
3930 	 * 3 or 4".
3931 	 * @param {Locale|string} locale locale to use when processing choice
3932 	 * formats with this string
3933 	 * @param {boolean=} sync [optional] whether to load the locale data synchronously
3934 	 * or not
3935 	 * @param {Object=} loadParams [optional] parameters to pass to the loader function
3936 	 * @param {function(*)=} onLoad [optional] function to call when the loading is done
3937 	 */
3938 	setLocale: function (locale, sync, loadParams, onLoad) {
3939 		if (typeof(locale) === 'object') {
3940 			this.locale = locale;
3941 		} else {
3942 			this.localeSpec = locale;
3943 			this.locale = new Locale(locale);
3944 		}
3945 
3946 		IString.loadPlurals(typeof(sync) !== 'undefined' ? sync : true, this.locale, loadParams, onLoad);
3947 	},
3948 
3949 	/**
3950 	 * Return the locale to use when processing choice formats. The locale
3951 	 * affects how number classes are interpretted. In some cultures,
3952 	 * the limit "few" maps to "any integer that ends in the digits 2 to 9" and
3953 	 * in yet others, "few" maps to "any integer that ends in the digits
3954 	 * 3 or 4".
3955 	 * @return {string} localespec to use when processing choice
3956 	 * formats with this string
3957 	 */
3958 	getLocale: function () {
3959 		return (this.locale ? this.locale.getSpec() : this.localeSpec) || ilib.getLocale();
3960 	},
3961 
3962 	/**
3963 	 * Return the number of code points in this string. This may be different
3964 	 * than the number of characters, as the UTF-16 encoding that Javascript
3965 	 * uses for its basis returns surrogate pairs separately. Two 2-byte
3966 	 * surrogate characters together make up one character/code point in
3967 	 * the supplementary character planes. If your string contains no
3968 	 * characters in the supplementary planes, this method will return the
3969 	 * same thing as the length() method.
3970 	 * @return {number} the number of code points in this string
3971 	 */
3972 	codePointLength: function () {
3973 		if (this.cpLength === -1) {
3974 			var it = this.iterator();
3975 			this.cpLength = 0;
3976 			while (it.hasNext()) {
3977 				this.cpLength++;
3978 				it.next();
3979 			};
3980 		}
3981 		return this.cpLength;
3982 	}
3983 };
3984 
3985 
3986 
3987 /*< LocaleInfo.js */
3988 /*
3989  * LocaleInfo.js - Encode locale-specific defaults
3990  * 
3991  * Copyright © 2012-2015, JEDLSoft
3992  *
3993  * Licensed under the Apache License, Version 2.0 (the "License");
3994  * you may not use this file except in compliance with the License.
3995  * You may obtain a copy of the License at
3996  *
3997  *     http://www.apache.org/licenses/LICENSE-2.0
3998  *
3999  * Unless required by applicable law or agreed to in writing, software
4000  * distributed under the License is distributed on an "AS IS" BASIS,
4001  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4002  *
4003  * See the License for the specific language governing permissions and
4004  * limitations under the License.
4005  */
4006 
4007 // !depends ilib.js Locale.js Utils.js
4008 
4009 // !data localeinfo
4010 
4011 
4012 /**
4013  * @class
4014  * Create a new locale info instance. Locale info instances give information about
4015  * the default settings for a particular locale. These settings may be overridden
4016  * by various parts of the code, and should be used as a fall-back setting of last
4017  * resort. <p>
4018  * 
4019  * The optional options object holds extra parameters if they are necessary. The
4020  * current list of supported options are:
4021  * 
4022  * <ul>
4023  * <li><i>onLoad</i> - a callback function to call when the locale info object is fully 
4024  * loaded. When the onLoad option is given, the localeinfo object will attempt to
4025  * load any missing locale data using the ilib loader callback.
4026  * When the constructor is done (even if the data is already preassembled), the 
4027  * onLoad function is called with the current instance as a parameter, so this
4028  * callback can be used with preassembled or dynamic loading or a mix of the two.
4029  * 
4030  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
4031  * asynchronously. If this option is given as "false", then the "onLoad"
4032  * callback must be given, as the instance returned from this constructor will
4033  * not be usable for a while. 
4034  *
4035  * <li><i>loadParams</i> - an object containing parameters to pass to the 
4036  * loader callback function when locale data is missing. The parameters are not
4037  * interpretted or modified in any way. They are simply passed along. The object 
4038  * may contain any property/value pairs as long as the calling code is in
4039  * agreement with the loader callback function as to what those parameters mean.
4040  * </ul>
4041  * 
4042  * If this copy of ilib is pre-assembled and all the data is already available, 
4043  * or if the data was already previously loaded, then this constructor will call
4044  * the onLoad callback immediately when the initialization is done. 
4045  * If the onLoad option is not given, this class will only attempt to load any
4046  * missing locale data synchronously.
4047  * 
4048  * 
4049  * @constructor
4050  * @see {ilib.setLoaderCallback} for information about registering a loader callback
4051  * function
4052  * @param {Locale|string=} locale the locale for which the info is sought, or undefined for
4053  * @param {Object=} options the locale for which the info is sought, or undefined for
4054  * the current locale
4055  */
4056 var LocaleInfo = function(locale, options) {
4057 	var sync = true,
4058 	    loadParams = undefined;
4059 	
4060 	/**
4061 	  @private 
4062 	  @type {{
4063 		calendar:string,
4064 		clock:string,
4065 		currency:string,
4066 		delimiter: {quotationStart:string,quotationEnd:string,alternateQuotationStart:string,alternateQuotationEnd:string},
4067 		firstDayOfWeek:number,
4068 		meridiems:string,
4069 		numfmt:{
4070 			currencyFormats:{common:string,commonNegative:string,iso:string,isoNegative:string},
4071 			decimalChar:string,
4072 			exponential:string,
4073 			groupChar:string,
4074 			negativenumFmt:string,
4075 			negativepctFmt:string,
4076 			pctChar:string,
4077 			pctFmt:string,
4078 			prigroupSize:number,
4079 			roundingMode:string,
4080 			script:string,
4081 			secgroupSize:number,
4082 			useNative:boolean
4083 		},
4084 		timezone:string,
4085 		units:string,
4086 		weekendEnd:number,
4087 		weekendStart:number,
4088 		paperSizes:{regular:string}
4089 	  }}
4090 	*/
4091 	this.info = LocaleInfo.defaultInfo;
4092 	
4093 	switch (typeof(locale)) {
4094 		case "string":
4095 			this.locale = new Locale(locale);
4096 			break;
4097 		default:
4098 		case "undefined":
4099 			this.locale = new Locale();
4100 			break;
4101 		case "object":
4102 			this.locale = locale;
4103 			break;
4104 	}
4105 	
4106 	if (options) {
4107 		if (typeof(options.sync) !== 'undefined') {
4108 			sync = !!options.sync;
4109 		}
4110 		
4111 		if (typeof(options.loadParams) !== 'undefined') {
4112 			loadParams = options.loadParams;
4113 		}
4114 	}
4115 
4116 	if (!ilib.data.cache.LocaleInfo) {
4117 		ilib.data.cache.LocaleInfo = {};
4118 	}
4119 
4120 	Utils.loadData({
4121 		object: "LocaleInfo", 
4122 		locale: this.locale, 
4123 		name: "localeinfo.json", 
4124 		sync: sync, 
4125 		loadParams: loadParams, 
4126 		callback: ilib.bind(this, function (info) {
4127 			if (!info) {
4128 				info = LocaleInfo.defaultInfo;
4129 				var spec = this.locale.getSpec().replace(/-/g, "_");
4130 				ilib.data.cache.LocaleInfo[spec] = info;
4131 			}
4132 			this.info = info;
4133 			if (options && typeof(options.onLoad) === 'function') {
4134 				options.onLoad(this);
4135 			}
4136 		})
4137 	});
4138 };
4139 
4140 LocaleInfo.defaultInfo = ilib.data.localeinfo;
4141 LocaleInfo.defaultInfo = LocaleInfo.defaultInfo || {
4142     "calendar": "gregorian",
4143     "clock": "24",
4144     "currency": "USD",
4145     "delimiter": {
4146         "quotationStart": "“",
4147         "quotationEnd": "”",
4148         "alternateQuotationStart": "‘",
4149         "alternateQuotationEnd": "’"
4150     },
4151     "firstDayOfWeek": 1,
4152     "meridiems": "gregorian",
4153     "numfmt": {
4154         "script": "Latn",
4155         "decimalChar": ".",
4156         "groupChar": ",",
4157         "pctChar": "%",
4158         "exponential": "E",
4159         "prigroupSize": 3,
4160         "currencyFormats": {
4161             "common": "{s} {n}",
4162             "commonNegative": "-{s} {n}",
4163             "iso": "{s} {n}",
4164             "isoNegative": "({s} {n})"
4165         },
4166         "negativenumFmt": "-{n}",
4167         "pctFmt": "{n}%",
4168         "negativepctFmt": "-{n}%",
4169         "roundingMode": "halfdown",
4170         "secGroupSize": null,
4171         "useNative": false
4172     },
4173     "paperSizes": {
4174         "regular": "A4"
4175     },
4176     "timezone": "Etc/UTC",
4177     "units": "metric",
4178     "weekendEnd": 0,
4179     "weekendStart": 6
4180 };
4181 
4182 LocaleInfo.prototype = {
4183     /**
4184      * Return the name of the locale's language in English.
4185      * @returns {string} the name of the locale's language in English
4186      */
4187     getLanguageName: function () {
4188     	return this.info["language.name"];	
4189     },
4190     
4191     /**
4192      * Return the name of the locale's region in English. If the locale
4193      * has no region, this returns undefined.
4194      * 
4195      * @returns {string|undefined} the name of the locale's region in English
4196      */
4197     getRegionName: function () {
4198     	return this.info["region.name"];	
4199     },
4200 
4201     /**
4202 	 * Return whether this locale commonly uses the 12- or the 24-hour clock.
4203 	 *  
4204 	 * @returns {string} "12" if the locale commonly uses a 12-hour clock, or "24"
4205 	 * if the locale commonly uses a 24-hour clock. 
4206 	 */
4207 	getClock: function() {
4208 		return this.info.clock;
4209 	},
4210 
4211 	/**
4212 	 * Return the locale that this info object was created with.
4213 	 * @returns {Locale} The locale spec of the locale used to construct this info instance
4214 	 */
4215 	getLocale: function () {
4216 		return this.locale;
4217 	},
4218 	
4219 	/**
4220 	 * Return the name of the measuring system that is commonly used in the given locale.
4221 	 * Valid values are "uscustomary", "imperial", and "metric".
4222 	 * 
4223 	 * @returns {string} The name of the measuring system commonly used in the locale
4224 	 */
4225 	getUnits: function () {
4226 		return this.info.units;
4227 	},
4228 	
4229 	/**
4230 	 * Return the name of the calendar that is commonly used in the given locale.
4231 	 * 
4232 	 * @returns {string} The name of the calendar commonly used in the locale
4233 	 */
4234 	getCalendar: function () {
4235 		return this.info.calendar;
4236 	},
4237 	
4238 	/**
4239 	 * Return the day of week that starts weeks in the current locale. Days are still
4240 	 * numbered the standard way with 0 for Sunday through 6 for Saturday, but calendars 
4241 	 * should be displayed and weeks calculated with the day of week returned from this 
4242 	 * function as the first day of the week.
4243 	 * 
4244 	 * @returns {number} the day of the week that starts weeks in the current locale.
4245 	 */
4246 	getFirstDayOfWeek: function () {
4247 		return this.info.firstDayOfWeek;
4248 	},
4249 	
4250 	/**
4251 	 * Return the day of week that starts weekend in the current locale. Days are still
4252 	 * numbered the standard way with 0 for Sunday through 6 for Saturday.
4253 	 * 
4254 	 * @returns {number} the day of the week that starts weeks in the current locale.
4255 	 */
4256 	getWeekEndStart: function () {
4257 		return this.info.weekendStart;
4258 	},
4259 
4260 	/**
4261 	 * Return the day of week that starts weekend in the current locale. Days are still
4262 	 * numbered the standard way with 0 for Sunday through 6 for Saturday.
4263 	 * 
4264 	 * @returns {number} the day of the week that starts weeks in the current locale.
4265 	 */
4266 	getWeekEndEnd: function () {
4267 		return this.info.weekendEnd;
4268 	},
4269 
4270 	/**
4271 	 * Return the default time zone for this locale. Many locales span across multiple
4272 	 * time zones. In this case, the time zone with the largest population is chosen
4273 	 * to represent the locale. This is obviously not that accurate, but then again,
4274 	 * this method's return value should only be used as a default anyways.
4275 	 * @returns {string} the default time zone for this locale.
4276 	 */
4277 	getTimeZone: function () {
4278 		return this.info.timezone;
4279 	},
4280 	
4281 	/**
4282 	 * Return the decimal separator for formatted numbers in this locale.
4283 	 * @returns {string} the decimal separator char
4284 	 */
4285 	getDecimalSeparator: function () {
4286 		return this.info.numfmt.decimalChar;
4287 	},
4288 	
4289 	/**
4290 	 * Return the decimal separator for formatted numbers in this locale for native script.
4291 	 * @returns {string} the decimal separator char
4292 	 */
4293 	getNativeDecimalSeparator: function () {
4294 		return (this.info.native_numfmt && this.info.native_numfmt.decimalChar) || this.info.numfmt.decimalChar;
4295 	},
4296 	
4297 	/**
4298 	 * Return the separator character used to separate groups of digits on the 
4299 	 * integer side of the decimal character.
4300 	 * @returns {string} the grouping separator char
4301 	 */
4302 	getGroupingSeparator: function () {
4303 		return this.info.numfmt.groupChar;
4304 	},
4305 
4306 	/**
4307 	 * Return the separator character used to separate groups of digits on the 
4308 	 * integer side of the decimal character for the native script if present other than the default script.
4309 	 * @returns {string} the grouping separator char
4310 	 */
4311 	getNativeGroupingSeparator: function () {
4312 		return (this.info.native_numfmt && this.info.native_numfmt.groupChar) || this.info.numfmt.groupChar;
4313 	},
4314 	
4315 	/**
4316 	 * Return the minimum number of digits grouped together on the integer side 
4317 	 * for the first (primary) group. 
4318 	 * In western European cultures, groupings are in 1000s, so the number of digits
4319 	 * is 3. 
4320 	 * @returns {number} the number of digits in a primary grouping, or 0 for no grouping
4321 	 */
4322 	getPrimaryGroupingDigits: function () {
4323 		return (typeof(this.info.numfmt.prigroupSize) !== 'undefined' && this.info.numfmt.prigroupSize) || 0;
4324 	},
4325 
4326 	/**
4327 	 * Return the minimum number of digits grouped together on the integer side
4328 	 * for the second or more (secondary) group.<p>
4329 	 *   
4330 	 * In western European cultures, all groupings are by 1000s, so the secondary
4331 	 * size should be 0 because there is no secondary size. In general, if this 
4332 	 * method returns 0, then all groupings are of the primary size.<p> 
4333 	 * 
4334 	 * For some other cultures, the first grouping (primary)
4335 	 * is 3 and any subsequent groupings (secondary) are two. So, 100000 would be
4336 	 * written as: "1,00,000".
4337 	 * 
4338 	 * @returns {number} the number of digits in a secondary grouping, or 0 for no 
4339 	 * secondary grouping. 
4340 	 */
4341 	getSecondaryGroupingDigits: function () {
4342 		return this.info.numfmt.secgroupSize || 0;
4343 	},
4344 
4345 	/**
4346 	 * Return the format template used to format percentages in this locale.
4347 	 * @returns {string} the format template for formatting percentages
4348 	 */
4349 	getPercentageFormat: function () {
4350 		return this.info.numfmt.pctFmt;
4351 	},
4352 
4353 	/**
4354 	 * Return the format template used to format percentages in this locale
4355 	 * with negative amounts.
4356 	 * @returns {string} the format template for formatting percentages
4357 	 */
4358 	getNegativePercentageFormat: function () {
4359 		return this.info.numfmt.negativepctFmt;
4360 	},
4361 
4362 	/**
4363 	 * Return the symbol used for percentages in this locale.
4364 	 * @returns {string} the symbol used for percentages in this locale
4365 	 */
4366 	getPercentageSymbol: function () {
4367 		return this.info.numfmt.pctChar || "%";
4368 	},
4369 
4370 	/**
4371 	 * Return the symbol used for exponential in this locale.
4372 	 * @returns {string} the symbol used for exponential in this locale
4373 	 */
4374 	getExponential: function () {
4375 		return this.info.numfmt.exponential;
4376 	},
4377 
4378 	/**
4379 	 * Return the symbol used for exponential in this locale for native script.
4380 	 * @returns {string} the symbol used for exponential in this locale for native script
4381 	 */
4382 	getNativeExponential: function () {
4383 		return (this.info.native_numfmt && this.info.native_numfmt.exponential) || this.info.numfmt.exponential;
4384 	},
4385 
4386 	/**
4387 	 * Return the symbol used for percentages in this locale for native script.
4388 	 * @returns {string} the symbol used for percentages in this locale for native script
4389 	 */
4390 	getNativePercentageSymbol: function () {
4391 		return (this.info.native_numfmt && this.info.native_numfmt.pctChar) || this.info.numfmt.pctChar || "%";
4392 	
4393 	},
4394 	/**
4395 	 * Return the format template used to format negative numbers in this locale.
4396 	 * @returns {string} the format template for formatting negative numbers
4397 	 */
4398 	getNegativeNumberFormat: function () { 
4399 		return this.info.numfmt.negativenumFmt;
4400 	},
4401 	
4402 	/**
4403 	 * Return an object containing the format templates for formatting currencies
4404 	 * in this locale. The object has a number of properties in it that each are
4405 	 * a particular style of format. Normally, this contains a "common" and an "iso"
4406 	 * style, but may contain others in the future.
4407 	 * @returns {Object} an object containing the format templates for currencies
4408 	 */
4409 	getCurrencyFormats: function () {
4410 		return this.info.numfmt.currencyFormats;
4411 	},
4412 	
4413 	/**
4414 	 * Return the currency that is legal in the locale, or which is most commonly 
4415 	 * used in regular commerce.
4416 	 * @returns {string} the ISO 4217 code for the currency of this locale
4417 	 */
4418 	getCurrency: function () {
4419 		return this.info.currency;
4420 	},
4421 	
4422 	/**
4423 	 * Return a string that describes the style of digits used by this locale.
4424 	 * Possible return values are:
4425 	 * <ul>
4426 	 * <li><i>western</i> - uses the regular western 10-based digits 0 through 9
4427 	 * <li><i>optional</i> - native 10-based digits exist, but in modern usage,
4428 	 * this locale most often uses western digits
4429 	 * <li><i>native</i> - native 10-based native digits exist and are used
4430 	 * regularly by this locale
4431 	 * <li><i>custom</i> - uses native digits by default that are not 10-based
4432 	 * </ul>
4433 	 * @returns {string} string that describes the style of digits used in this locale
4434 	 */
4435 	getDigitsStyle: function () {
4436 		if (this.info.numfmt && this.info.numfmt.useNative) {
4437 			return "native";
4438 		}
4439 		if (typeof(this.info.native_numfmt) !== 'undefined') {
4440 			return "optional";
4441 		}
4442 		return "western";
4443 	},
4444 	
4445 	/**
4446 	 * Return the digits of the default script if they are defined.
4447 	 * If not defined, the default should be the regular "Arabic numerals"
4448 	 * used in the Latin script. (0-9)
4449 	 * @returns {string|undefined} the digits used in the default script 
4450 	 */
4451 	getDigits: function () {
4452 		return this.info.numfmt.digits;
4453 	},
4454 	
4455 	/**
4456 	 * Return the digits of the native script if they are defined. 
4457 	 * @returns {string|undefined} the digits used in the default script 
4458 	 */
4459 	getNativeDigits: function () {
4460 		return (this.info.numfmt.useNative && this.info.numfmt.digits) || (this.info.native_numfmt && this.info.native_numfmt.digits);
4461 	},
4462 	
4463 	/**
4464 	 * If this locale typically uses a different type of rounding for numeric
4465 	 * formatting other than halfdown, especially for currency, then it can be 
4466 	 * specified in the localeinfo. If the locale uses the default, then this 
4467 	 * method returns undefined. The locale's rounding method overrides the 
4468 	 * rounding method for the currency itself, which can sometimes shared 
4469 	 * between various locales so it is less specific.
4470 	 * @returns {string} the name of the rounding mode typically used in this
4471 	 * locale, or "halfdown" if the locale does not override the default
4472 	 */
4473 	getRoundingMode: function () {
4474 		return this.info.numfmt.roundingMode;
4475 	},
4476 	
4477 	/**
4478 	 * Return the default script used to write text in the language of this 
4479 	 * locale. Text for most languages is written in only one script, but there
4480 	 * are some languages where the text can be written in a number of scripts,
4481 	 * depending on a variety of things such as the region, ethnicity, religion, 
4482 	 * etc. of the author. This method returns the default script for the
4483 	 * locale, in which the language is most commonly written.<p> 
4484 	 * 
4485 	 * The script is returned as an ISO 15924 4-letter code.
4486 	 * 
4487 	 * @returns {string} the ISO 15924 code for the default script used to write
4488 	 * text in this locale 
4489 	 */
4490 	getDefaultScript: function() {
4491 		return (this.info.scripts) ? this.info.scripts[0] : "Latn";
4492 	},
4493 	
4494 	/**
4495 	 * Return the script used for the current locale. If the current locale
4496 	 * explicitly defines a script, then this script is returned. If not, then 
4497 	 * the default script for the locale is returned.
4498 	 * 
4499 	 * @see LocaleInfo.getDefaultScript
4500 	 * @returns {string} the ISO 15924 code for the script used to write
4501 	 * text in this locale
4502 	 */
4503 	getScript: function() {
4504 		return this.locale.getScript() || this.getDefaultScript(); 
4505 	},
4506 	
4507 	/**
4508 	 * Return an array of script codes which are used to write text in the current
4509 	 * language. Text for most languages is written in only one script, but there
4510 	 * are some languages where the text can be written in a number of scripts,
4511 	 * depending on a variety of things such as the region, ethnicity, religion, 
4512 	 * etc. of the author. This method returns an array of script codes in which 
4513 	 * the language is commonly written.
4514 	 * 
4515 	 * @returns {Array.<string>} an array of ISO 15924 codes for the scripts used 
4516 	 * to write text in this language
4517 	 */
4518 	getAllScripts: function() {
4519 		return this.info.scripts || ["Latn"];
4520 	},
4521 	
4522 	/**
4523 	 * Return the default style of meridiems used in this locale. Meridiems are 
4524 	 * times of day like AM/PM. In a few locales with some calendars, for example
4525 	 * Amharic/Ethiopia using the Ethiopic calendar, the times of day may be
4526 	 * split into different segments than simple AM/PM as in the Gregorian 
4527 	 * calendar. Only a few locales are like that. For most locales, formatting 
4528 	 * a Gregorian date will use the regular Gregorian AM/PM meridiems.
4529 	 *  
4530 	 * @returns {string} the default meridiems style used in this locale. Possible
4531 	 * values are "gregorian", "chinese", and "ethiopic"
4532 	 */
4533 	getMeridiemsStyle: function () {
4534 		return this.info.meridiems || "gregorian";
4535 	},	
4536 	/**
4537 	 * Return the default PaperSize information in this locale.
4538 	 * @returns {string} default PaperSize in this locale
4539 	 */
4540 	getPaperSize: function () {
4541 		return this.info.paperSizes.regular;
4542 	},
4543 	/**
4544 	 * Return the default Delimiter QuotationStart information in this locale.
4545 	 * @returns {string} default QuotationStart in this locale
4546 	 */
4547 	getDelimiterQuotationStart: function () {
4548 		return this.info.delimiter.quotationStart;
4549 	},
4550 	/**
4551 	 * Return the default Delimiter QuotationEnd information in this locale.
4552 	 * @returns {string} default QuotationEnd in this locale
4553 	 */
4554 	getDelimiterQuotationEnd: function () {
4555 		return this.info.delimiter.quotationEnd;
4556 	}
4557 };
4558 
4559 
4560 
4561 /*< Calendar.js */
4562 /*
4563  * Calendar.js - Represent a calendar object.
4564  * 
4565  * Copyright © 2012-2015, JEDLSoft
4566  *
4567  * Licensed under the Apache License, Version 2.0 (the "License");
4568  * you may not use this file except in compliance with the License.
4569  * You may obtain a copy of the License at
4570  *
4571  *     http://www.apache.org/licenses/LICENSE-2.0
4572  *
4573  * Unless required by applicable law or agreed to in writing, software
4574  * distributed under the License is distributed on an "AS IS" BASIS,
4575  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4576  *
4577  * See the License for the specific language governing permissions and
4578  * limitations under the License.
4579  */
4580 
4581 /**
4582  * @class
4583  * Superclass for all calendar subclasses that contains shared 
4584  * functionality. This class is never instantiated on its own. Instead,
4585  * you should use the {@link CalendarFactory} function to manufacture a new
4586  * instance of a subclass of Calendar. 
4587  * 
4588  * @private
4589  * @constructor
4590  */
4591 var Calendar = function() {
4592 };
4593 
4594 /* place for the subclasses to put their constructors so that the factory method
4595  * can find them. Do this to add your calendar after it's defined: 
4596  * Calendar._constructors["mytype"] = Calendar.MyTypeConstructor;
4597  */
4598 Calendar._constructors = {};
4599 
4600 Calendar.prototype = {
4601 	/**
4602 	 * Return the type of this calendar.
4603 	 * 
4604 	 * @return {string} the name of the type of this calendar 
4605 	 */
4606 	getType: function() {
4607 		throw "Cannot call methods of abstract class Calendar";
4608 	},
4609 	
4610 	/**
4611 	 * Return the number of months in the given year. The number of months in a year varies
4612 	 * for some luni-solar calendars because in some years, an extra month is needed to extend the 
4613 	 * days in a year to an entire solar year. The month is represented as a 1-based number
4614 	 * where 1=first month, 2=second month, etc.
4615 	 * 
4616 	 * @param {number} year a year for which the number of months is sought
4617 	 * @return {number} The number of months in the given year
4618 	 */
4619 	getNumMonths: function(year) {
4620 		throw "Cannot call methods of abstract class Calendar";
4621 	},
4622 	
4623 	/**
4624 	 * Return the number of days in a particular month in a particular year. This function
4625 	 * can return a different number for a month depending on the year because of things
4626 	 * like leap years.
4627 	 * 
4628 	 * @param {number} month the month for which the length is sought
4629 	 * @param {number} year the year within which that month can be found
4630 	 * @return {number} the number of days within the given month in the given year
4631 	 */
4632 	getMonLength: function(month, year) {
4633 		throw "Cannot call methods of abstract class Calendar";
4634 	},
4635 	
4636 	/**
4637 	 * Return true if the given year is a leap year in this calendar.
4638 	 * The year parameter may be given as a number.
4639 	 * 
4640 	 * @param {number} year the year for which the leap year information is being sought
4641 	 * @return {boolean} true if the given year is a leap year
4642 	 */
4643 	isLeapYear: function(year) {
4644 		throw "Cannot call methods of abstract class Calendar";
4645 	}
4646 };
4647 
4648 
4649 /*< CalendarFactory.js */
4650 /*
4651  * CalendarFactory.js - Constructs new instances of the right subclass of Calendar
4652  * 
4653  * Copyright © 2015, JEDLSoft
4654  *
4655  * Licensed under the Apache License, Version 2.0 (the "License");
4656  * you may not use this file except in compliance with the License.
4657  * You may obtain a copy of the License at
4658  *
4659  *     http://www.apache.org/licenses/LICENSE-2.0
4660  *
4661  * Unless required by applicable law or agreed to in writing, software
4662  * distributed under the License is distributed on an "AS IS" BASIS,
4663  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4664  *
4665  * See the License for the specific language governing permissions and
4666  * limitations under the License.
4667  */
4668 
4669 /* !depends
4670 ilib.js
4671 Locale.js
4672 LocaleInfo.js
4673 Calendar.js
4674 */
4675 
4676 
4677 /**
4678  * Factory method to create a new instance of a calendar subclass.<p>
4679  * 
4680  * The options parameter can be an object that contains the following
4681  * properties:
4682  * 
4683  * <ul>
4684  * <li><i>type</i> - specify the type of the calendar desired. The
4685  * list of valid values changes depending on which calendars are 
4686  * defined. When assembling your iliball.js, include those calendars 
4687  * you wish to use in your program or web page, and they will register 
4688  * themselves with this factory method. The "official", "gregorian",
4689  * and "julian" calendars are all included by default, as they are the
4690  * standard calendars for much of the world.
4691  * <li><i>locale</i> - some calendars vary depending on the locale.
4692  * For example, the "official" calendar transitions from a Julian-style
4693  * calendar to a Gregorian-style calendar on a different date for
4694  * each country, as the governments of those countries decided to
4695  * adopt the Gregorian calendar at different times.
4696  *  
4697  * <li><i>onLoad</i> - a callback function to call when the calendar object is fully 
4698  * loaded. When the onLoad option is given, the calendar factory will attempt to
4699  * load any missing locale data using the ilib loader callback.
4700  * When the constructor is done (even if the data is already preassembled), the 
4701  * onLoad function is called with the current instance as a parameter, so this
4702  * callback can be used with preassembled or dynamic loading or a mix of the two.
4703  * 
4704  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
4705  * asynchronously. If this option is given as "false", then the "onLoad"
4706  * callback must be given, as the instance returned from this constructor will
4707  * not be usable for a while.
4708  *  
4709  * <li><i>loadParams</i> - an object containing parameters to pass to the 
4710  * loader callback function when locale data is missing. The parameters are not
4711  * interpretted or modified in any way. They are simply passed along. The object 
4712  * may contain any property/value pairs as long as the calling code is in
4713  * agreement with the loader callback function as to what those parameters mean.
4714  * </ul>
4715  * 
4716  * If a locale is specified, but no type, then the calendar that is default for
4717  * the locale will be instantiated and returned. If neither the type nor
4718  * the locale are specified, then the calendar for the default locale will
4719  * be used. 
4720  * 
4721  * @static
4722  * @param {Object=} options options controlling the construction of this instance, or
4723  * undefined to use the default options
4724  * @return {Calendar} an instance of a calendar object of the appropriate type
4725  */
4726 var CalendarFactory = function (options) {
4727 	var locale,
4728 		type,
4729 		sync = true,
4730 		instance;
4731 
4732 	if (options) {
4733 		if (options.locale) {
4734 			locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
4735 		}
4736 		
4737 		type = options.type || options.calendar;
4738 		
4739 		if (typeof(options.sync) === 'boolean') {
4740 			sync = options.sync;
4741 		}
4742 	}
4743 	
4744 	if (!locale) {
4745 		locale = new Locale();	// default locale
4746 	}
4747 	
4748 	if (!type) {
4749 		new LocaleInfo(locale, {
4750 			sync: sync,
4751 			loadParams: options && options.loadParams,
4752 			onLoad: function(info) {
4753 				type = info.getCalendar();
4754 				
4755 				instance = CalendarFactory._init(type, options);
4756 			}
4757 		});
4758 	} else {
4759 		instance = CalendarFactory._init(type, options);
4760 	}
4761 	
4762 	return instance;
4763 };
4764 
4765 /**
4766  * Map calendar names to classes to initialize in the dynamic code model.
4767  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
4768  * @private
4769  */
4770 CalendarFactory._dynMap = {
4771 	"coptic":       "Coptic",
4772 	"ethiopic":     "Ethiopic",
4773 	"gregorian":    "Gregorian",
4774 	"han":          "Han",
4775 	"hebrew":       "Hebrew",
4776 	"islamic":      "Islamic",
4777 	"julian":       "Julian",
4778 	"persian":      "Persian",
4779 	"persian-algo": "PersianAlgo",
4780 	"thaisolar":    "ThaiSolar"
4781 };
4782 
4783 /**
4784  * Dynamically load the code for a calendar and calendar class if necessary.
4785  * @protected
4786  */
4787 CalendarFactory._dynLoadCalendar = function (name) {
4788 	if (!Calendar._constructors[name]) {
4789 		var entry = CalendarFactory._dynMap[name];
4790 		if (entry) {
4791 			Calendar._constructors[name] = require("./" + entry + "Cal.js");
4792 		}
4793 	}
4794 	return Calendar._constructors[name];
4795 };
4796 
4797 /** @private */
4798 CalendarFactory._init = function(type, options) {
4799 	var cons;
4800 	
4801 	if (ilib.isDynCode()) {
4802 		CalendarFactory._dynLoadCalendar(type);
4803 	}
4804 	
4805 	cons = Calendar._constructors[type];
4806 	
4807 	// pass the same options through to the constructor so the subclass
4808 	// has the ability to do something with if it needs to
4809     if (!cons && typeof(options.onLoad) === "function") {
4810         options.onLoad(undefined);
4811     }
4812 	return cons && new cons(options);
4813 };
4814 
4815 /**
4816  * Return an array of known calendar types that the factory method can instantiate.
4817  * 
4818  * @return {Array.<string>} an array of calendar types
4819  */
4820 CalendarFactory.getCalendars = function () {
4821 	var arr = [],
4822 		c;
4823 	
4824 	if (ilib.isDynCode()) {
4825 		for (c in CalendarFactory._dynMap) {
4826 			CalendarFactory._dynLoadCalendar(c);
4827 		}
4828 	}
4829 	
4830 	for (c in Calendar._constructors) {
4831 		if (c && Calendar._constructors[c]) {
4832 			arr.push(c); // code like a pirate
4833 		}
4834 	}
4835 	
4836 	return arr;
4837 };
4838 
4839 
4840 
4841 /*< IDate.js */
4842 /*
4843  * IDate.js - Represent a date in any calendar. This class is subclassed for each 
4844  * calendar and includes some shared functionality.
4845  * 
4846  * Copyright © 2012-2015, JEDLSoft
4847  *
4848  * Licensed under the Apache License, Version 2.0 (the "License");
4849  * you may not use this file except in compliance with the License.
4850  * You may obtain a copy of the License at
4851  *
4852  *     http://www.apache.org/licenses/LICENSE-2.0
4853  *
4854  * Unless required by applicable law or agreed to in writing, software
4855  * distributed under the License is distributed on an "AS IS" BASIS,
4856  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4857  *
4858  * See the License for the specific language governing permissions and
4859  * limitations under the License.
4860  */
4861 
4862 /* !depends LocaleInfo.js */
4863 
4864 
4865 /**
4866  * @class
4867  * Superclass for all the calendar date classes that contains shared 
4868  * functionality. This class is never instantiated on its own. Instead,
4869  * you should use the {@link DateFactory} function to manufacture a new
4870  * instance of a subclass of IDate. This class is called IDate for "ilib
4871  * date" so that it does not conflict with the built-in Javascript Date
4872  * class.
4873  * 
4874  * @private
4875  * @constructor
4876  * @param {Object=} options The date components to initialize this date with
4877  */
4878 var IDate = function(options) {
4879 };
4880 
4881 /* place for the subclasses to put their constructors so that the factory method
4882  * can find them. Do this to add your date after it's defined: 
4883  * IDate._constructors["mytype"] = IDate.MyTypeConstructor;
4884  */
4885 IDate._constructors = {};
4886 
4887 IDate.prototype = {
4888 	getType: function() {
4889 		return "date";
4890 	},
4891 	
4892 	/**
4893 	 * Return the unix time equivalent to this date instance. Unix time is
4894 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC (Gregorian). This 
4895 	 * method only returns a valid number for dates between midnight, 
4896 	 * Jan 1, 1970 UTC (Gregorian) and Jan 19, 2038 at 3:14:07am UTC (Gregorian) when 
4897 	 * the unix time runs out. If this instance encodes a date outside of that range, 
4898 	 * this method will return -1. For date types that are not Gregorian, the point 
4899 	 * in time represented by this date object will only give a return value if it
4900 	 * is in the correct range in the Gregorian calendar as given previously.
4901 	 * 
4902 	 * @return {number} a number giving the unix time, or -1 if the date is outside the
4903 	 * valid unix time range
4904 	 */
4905 	getTime: function() {
4906 		return this.rd.getTime(); 
4907 	},
4908 	
4909 	/**
4910 	 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is
4911 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time
4912 	 * (or the type "time_t" in C/C++) is only encoded with an unsigned 32 bit integer, and thus 
4913 	 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 
4914 	 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 
4915 	 * after Jan 1, 1970, and even more interestingly, 100 million days worth of time before
4916 	 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 
4917 	 * range. If this instance encodes a date outside of that range, this method will return
4918 	 * NaN.
4919 	 * 
4920 	 * @return {number} a number giving the extended unix time, or Nan if the date is outside 
4921 	 * the valid extended unix time range
4922 	 */
4923 	getTimeExtended: function() {
4924 		return this.rd.getTimeExtended();
4925 	},
4926 
4927 	/**
4928 	 * Set the time of this instance according to the given unix time. Unix time is
4929 	 * the number of milliseconds since midnight on Jan 1, 1970.
4930 	 * 
4931 	 * @param {number} millis the unix time to set this date to in milliseconds 
4932 	 */
4933 	setTime: function(millis) {
4934 		this.rd = this.newRd({
4935 			unixtime: millis,
4936 			cal: this.cal
4937 		});
4938 		this._calcDateComponents();
4939 	},
4940 	
4941 	getDays: function() {
4942 		return this.day;
4943 	},
4944 	getMonths: function() {
4945 		return this.month;
4946 	},
4947 	getYears: function() {
4948 		return this.year;
4949 	},
4950 	getHours: function() {
4951 		return this.hour;
4952 	},
4953 	getMinutes: function() {
4954 		return this.minute;
4955 	},
4956 	getSeconds: function() {
4957 		return this.second;
4958 	},
4959 	getMilliseconds: function() {
4960 		return this.millisecond;
4961 	},
4962 	getEra: function() {
4963 		return (this.year < 1) ? -1 : 1;
4964 	},
4965 
4966 	setDays: function(day) {
4967 		this.day = parseInt(day, 10) || 1;
4968 		this.rd._setDateComponents(this);
4969 	},
4970 	setMonths: function(month) {
4971 		this.month = parseInt(month, 10) || 1;
4972 		this.rd._setDateComponents(this);
4973 	},
4974 	setYears: function(year) {
4975 		this.year = parseInt(year, 10) || 0;
4976 		this.rd._setDateComponents(this);
4977 	},
4978 	
4979 	setHours: function(hour) {
4980 		this.hour = parseInt(hour, 10) || 0;
4981 		this.rd._setDateComponents(this);
4982 	},
4983 	setMinutes: function(minute) {
4984 		this.minute = parseInt(minute, 10) || 0;
4985 		this.rd._setDateComponents(this);
4986 	},
4987 	setSeconds: function(second) {
4988 		this.second = parseInt(second, 10) || 0;
4989 		this.rd._setDateComponents(this);
4990 	},
4991 	setMilliseconds: function(milli) {
4992 		this.millisecond = parseInt(milli, 10) || 0;
4993 		this.rd._setDateComponents(this);
4994 	},
4995 	
4996 	/**
4997 	 * Return a new date instance in the current calendar that represents the first instance 
4998 	 * of the given day of the week before the current date. The day of the week is encoded
4999 	 * as a number where 0 = Sunday, 1 = Monday, etc.
5000 	 * 
5001 	 * @param {number} dow the day of the week before the current date that is being sought
5002 	 * @return {IDate} the date being sought
5003 	 */
5004 	before: function (dow) {
5005 		return new this.constructor({
5006 			rd: this.rd.before(dow, this.offset),
5007 			timezone: this.timezone
5008 		});
5009 	},
5010 	
5011 	/**
5012 	 * Return a new date instance in the current calendar that represents the first instance 
5013 	 * of the given day of the week after the current date. The day of the week is encoded
5014 	 * as a number where 0 = Sunday, 1 = Monday, etc.
5015 	 * 
5016 	 * @param {number} dow the day of the week after the current date that is being sought
5017 	 * @return {IDate} the date being sought
5018 	 */
5019 	after: function (dow) {
5020 		return new this.constructor({
5021 			rd: this.rd.after(dow, this.offset),
5022 			timezone: this.timezone
5023 		});
5024 	},
5025 
5026 	/**
5027 	 * Return a new Gregorian date instance that represents the first instance of the 
5028 	 * given day of the week on or before the current date. The day of the week is encoded
5029 	 * as a number where 0 = Sunday, 1 = Monday, etc.
5030 	 * 
5031 	 * @param {number} dow the day of the week on or before the current date that is being sought
5032 	 * @return {IDate} the date being sought
5033 	 */
5034 	onOrBefore: function (dow) {
5035 		return new this.constructor({
5036 			rd: this.rd.onOrBefore(dow, this.offset),
5037 			timezone: this.timezone
5038 		});
5039 	},
5040 
5041 	/**
5042 	 * Return a new Gregorian date instance that represents the first instance of the 
5043 	 * given day of the week on or after the current date. The day of the week is encoded
5044 	 * as a number where 0 = Sunday, 1 = Monday, etc.
5045 	 * 
5046 	 * @param {number} dow the day of the week on or after the current date that is being sought
5047 	 * @return {IDate} the date being sought
5048 	 */
5049 	onOrAfter: function (dow) {
5050 		return new this.constructor({
5051 			rd: this.rd.onOrAfter(dow, this.offset),
5052 			timezone: this.timezone
5053 		});
5054 	},
5055 	
5056 	/**
5057 	 * Return a Javascript Date object that is equivalent to this date
5058 	 * object.
5059 	 * 
5060 	 * @return {Date|undefined} a javascript Date object
5061 	 */
5062 	getJSDate: function() {
5063 		var unix = this.rd.getTimeExtended();
5064 		return isNaN(unix) ? undefined : new Date(unix); 
5065 	},
5066 	
5067 	/**
5068 	 * Return the Rata Die (fixed day) number of this date.
5069 	 * 
5070 	 * @protected
5071 	 * @return {number} the rd date as a number
5072 	 */
5073 	getRataDie: function() {
5074 		return this.rd.getRataDie();
5075 	},
5076 	
5077 	/**
5078 	 * Set the date components of this instance based on the given rd.
5079 	 * @protected
5080 	 * @param {number} rd the rata die date to set
5081 	 */
5082 	setRd: function (rd) {
5083 		this.rd = this.newRd({
5084 			rd: rd,
5085 			cal: this.cal
5086 		});
5087 		this._calcDateComponents();
5088 	},
5089 	
5090 	/**
5091 	 * Return the Julian Day equivalent to this calendar date as a number.
5092 	 * 
5093 	 * @return {number} the julian date equivalent of this date
5094 	 */
5095 	getJulianDay: function() {
5096 		return this.rd.getJulianDay();
5097 	},
5098 	
5099 	/**
5100 	 * Set the date of this instance using a Julian Day.
5101 	 * @param {number|JulianDay} date the Julian Day to use to set this date
5102 	 */
5103 	setJulianDay: function (date) {
5104 		this.rd = this.newRd({
5105 			julianday: (typeof(date) === 'object') ? date.getDate() : date,
5106 			cal: this.cal
5107 		});
5108 		this._calcDateComponents();
5109 	},
5110 
5111 	/**
5112 	 * Return the time zone associated with this date, or 
5113 	 * undefined if none was specified in the constructor.
5114 	 * 
5115 	 * @return {string|undefined} the name of the time zone for this date instance
5116 	 */
5117 	getTimeZone: function() {
5118 		return this.timezone || "local";
5119 	},
5120 	
5121 	/**
5122 	 * Set the time zone associated with this date.
5123 	 * @param {string=} tzName the name of the time zone to set into this date instance,
5124 	 * or "undefined" to unset the time zone 
5125 	 */
5126 	setTimeZone: function (tzName) {
5127 		if (!tzName || tzName === "") {
5128 			// same as undefining it
5129 			this.timezone = undefined;
5130 			this.tz = undefined;
5131 		} else if (typeof(tzName) === 'string') {
5132 			this.timezone = tzName;
5133 			this.tz = undefined;
5134 			// assuming the same UTC time, but a new time zone, now we have to 
5135 			// recalculate what the date components are
5136 			this._calcDateComponents();
5137 		}
5138 	},
5139 	
5140 	/**
5141 	 * Return the rd number of the first Sunday of the given ISO year.
5142 	 * @protected
5143 	 * @param {number} year the year for which the first Sunday is being sought
5144 	 * @return {number} the rd of the first Sunday of the ISO year
5145 	 */
5146 	firstSunday: function (year) {
5147 		var firstDay = this.newRd({
5148 			year: year,
5149 			month: 1,
5150 			day: 1,
5151 			hour: 0,
5152 			minute: 0,
5153 			second: 0,
5154 			millisecond: 0,
5155 			cal: this.cal
5156 		});
5157 		var firstThu = this.newRd({
5158 			rd: firstDay.onOrAfter(4),
5159 			cal: this.cal
5160 		});
5161 		return firstThu.before(0);
5162 	},
5163 	
5164 	/**
5165 	 * Return the ISO 8601 week number in the current year for the current date. The week
5166 	 * number ranges from 0 to 55, as some years have 55 weeks assigned to them in some
5167 	 * calendars.
5168 	 * 
5169 	 * @return {number} the week number for the current date
5170 	 */
5171 	getWeekOfYear: function() {
5172 		var rd = Math.floor(this.rd.getRataDie());
5173 		var year = this._calcYear(rd + this.offset);
5174 		var yearStart = this.firstSunday(year);
5175 		var nextYear;
5176 		
5177 		// if we have a January date, it may be in this ISO year or the previous year
5178 		if (rd < yearStart) {
5179 			yearStart = this.firstSunday(year-1);
5180 		} else {
5181 			// if we have a late December date, it may be in this ISO year, or the next year
5182 			nextYear = this.firstSunday(year+1);
5183 			if (rd >= nextYear) {
5184 				yearStart = nextYear;
5185 			}
5186 		}
5187 		
5188 		return Math.floor((rd-yearStart)/7) + 1;
5189 	},
5190 	
5191 	/**
5192 	 * Return the ordinal number of the week within the month. The first week of a month is
5193 	 * the first one that contains 4 or more days in that month. If any days precede this
5194 	 * first week, they are marked as being in week 0. This function returns values from 0
5195 	 * through 6.<p>
5196 	 * 
5197 	 * The locale is a required parameter because different locales that use the same 
5198 	 * Gregorian calendar consider different days of the week to be the beginning of
5199 	 * the week. This can affect the week of the month in which some days are located.
5200 	 * 
5201 	 * @param {Locale|string} locale the locale or locale spec to use when figuring out 
5202 	 * the first day of the week
5203 	 * @return {number} the ordinal number of the week within the current month
5204 	 */
5205 	getWeekOfMonth: function(locale) {
5206 		var li = new LocaleInfo(locale);
5207 		
5208 		var first = this.newRd({
5209 			year: this._calcYear(this.rd.getRataDie()+this.offset),
5210 			month: this.getMonths(),
5211 			day: 1,
5212 			hour: 0,
5213 			minute: 0,
5214 			second: 0,
5215 			millisecond: 0,
5216 			cal: this.cal
5217 		});
5218 		var weekStart = first.onOrAfter(li.getFirstDayOfWeek());
5219 		
5220 		if (weekStart - first.getRataDie() > 3) {
5221 			// if the first week has 4 or more days in it of the current month, then consider
5222 			// that week 1. Otherwise, it is week 0. To make it week 1, move the week start
5223 			// one week earlier.
5224 			weekStart -= 7;
5225 		}
5226 		return Math.floor((this.rd.getRataDie() - weekStart) / 7) + 1;
5227 	}
5228 };
5229 
5230 
5231 /*< JulianDay.js */
5232 /*
5233  * JulianDay.js - A Julian Day object.
5234  * 
5235  * Copyright © 2012-2015, JEDLSoft
5236  *
5237  * Licensed under the Apache License, Version 2.0 (the "License");
5238  * you may not use this file except in compliance with the License.
5239  * You may obtain a copy of the License at
5240  *
5241  *     http://www.apache.org/licenses/LICENSE-2.0
5242  *
5243  * Unless required by applicable law or agreed to in writing, software
5244  * distributed under the License is distributed on an "AS IS" BASIS,
5245  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5246  *
5247  * See the License for the specific language governing permissions and
5248  * limitations under the License.
5249  */
5250 
5251 /**
5252  * @class
5253  * A Julian Day class. A Julian Day is a date based on the Julian Day count
5254  * of time invented by Joseph Scaliger in 1583 for use with astronomical calculations. 
5255  * Do not confuse it with a date in the Julian calendar, which it has very
5256  * little in common with. The naming is unfortunately close, and comes from history.<p>
5257  * 
5258  * 
5259  * @constructor
5260  * @param {number} num the Julian Day expressed as a floating point number 
5261  */
5262 var JulianDay = function(num) {
5263 	this.jd = num;
5264 	this.days = Math.floor(this.jd);
5265 	this.frac = num - this.days;
5266 };
5267 
5268 JulianDay.prototype = {
5269 	/**
5270 	 * Return the integral portion of this Julian Day instance. This corresponds to
5271 	 * the number of days since the beginning of the epoch.
5272 	 * 
5273 	 * @return {number} the integral portion of this Julian Day
5274 	 */
5275 	getDays: function() {
5276 		return this.days;
5277 	},
5278 	
5279 	/**
5280 	 * Set the date of this Julian Day instance.
5281 	 * 
5282 	 * @param {number} days the julian date expressed as a floating point number
5283 	 */
5284 	setDays: function(days) {
5285 		this.days = Math.floor(days);
5286 		this.jd = this.days + this.frac;
5287 	},
5288 	
5289 	/**
5290 	 * Return the fractional portion of this Julian Day instance. This portion 
5291 	 * corresponds to the time of day for the instance.
5292 	 */
5293 	getDayFraction: function() {
5294 		return this.frac;
5295 	},
5296 	
5297 	/**
5298 	 * Set the fractional part of the Julian Day. The fractional part represents
5299 	 * the portion of a fully day. Julian dates start at noon, and proceed until
5300 	 * noon of the next day. That would mean midnight is represented as a fractional
5301 	 * part of 0.5.
5302 	 * 
5303 	 * @param {number} fraction The fractional part of the Julian date
5304 	 */
5305 	setDayFraction: function(fraction) {
5306 		var t = Math.floor(fraction);
5307 		this.frac = fraction - t;
5308 		this.jd = this.days + this.frac;
5309 	},
5310 	
5311 	/** 
5312 	 * Return the Julian Day expressed as a floating point number.
5313 	 * @return {number} the Julian Day as a number
5314 	 */
5315 	getDate: function () {
5316 		return this.jd;
5317 	},
5318 	
5319 	/**
5320 	 * Set the date of this Julian Day instance.
5321 	 * 
5322 	 * @param {number} num the numeric Julian Day to set into this instance
5323 	 */
5324 	setDate: function (num) {
5325 		this.jd = num;
5326 	},
5327 	
5328 	/**
5329 	 * Add an offset to the current date instance. The offset should be expressed in
5330 	 * terms of Julian days. That is, each integral unit represents one day of time, and
5331 	 * fractional part represents a fraction of a regular 24-hour day.
5332 	 * 
5333 	 * @param {number} offset an amount to add (or subtract) to the current result instance.
5334 	 */
5335 	addDate: function(offset) {
5336 		if (typeof(offset) === 'number') {
5337 			this.jd += offset;
5338 			this.days = Math.floor(this.jd);
5339 			this.frac = this.jd - this.days;
5340 		}
5341 	}
5342 };
5343 
5344 
5345 
5346 /*< DateFactory.js */
5347 /*
5348  * DateFactory.js - Factory class to create the right subclasses of a date for any 
5349  * calendar or locale.
5350  * 
5351  * Copyright © 2012-2015, JEDLSoft
5352  *
5353  * Licensed under the Apache License, Version 2.0 (the "License");
5354  * you may not use this file except in compliance with the License.
5355  * You may obtain a copy of the License at
5356  *
5357  *     http://www.apache.org/licenses/LICENSE-2.0
5358  *
5359  * Unless required by applicable law or agreed to in writing, software
5360  * distributed under the License is distributed on an "AS IS" BASIS,
5361  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5362  *
5363  * See the License for the specific language governing permissions and
5364  * limitations under the License.
5365  */
5366 
5367 /* !depends ilib.js Locale.js LocaleInfo.js JulianDay.js JSUtils.js CalendarFactory.js IDate.js */
5368 
5369 
5370 
5371 // Statically depend on these even though we don't use them
5372 // to guarantee they are loaded into the cache already.
5373 
5374 /**
5375  * Factory method to create a new instance of a date subclass.<p>
5376  * 
5377  * The options parameter can be an object that contains the following
5378  * properties:
5379  * 
5380  * <ul>
5381  * <li><i>type</i> - specify the type/calendar of the date desired. The
5382  * list of valid values changes depending on which calendars are 
5383  * defined. When assembling your iliball.js, include those date type 
5384  * you wish to use in your program or web page, and they will register 
5385  * themselves with this factory method. The "gregorian",
5386  * and "julian" calendars are all included by default, as they are the
5387  * standard calendars for much of the world. If not specified, the type
5388  * of the date returned is the one that is appropriate for the locale.
5389  * This property may also be given as "calendar" instead of "type".
5390  * 
5391  * <li><i>onLoad</i> - a callback function to call when the date object is fully 
5392  * loaded. When the onLoad option is given, the date factory will attempt to
5393  * load any missing locale data using the ilib loader callback.
5394  * When the constructor is done (even if the data is already preassembled), the 
5395  * onLoad function is called with the current instance as a parameter, so this
5396  * callback can be used with preassembled or dynamic loading or a mix of the two.
5397  * 
5398  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
5399  * asynchronously. If this option is given as "false", then the "onLoad"
5400  * callback must be given, as the instance returned from this constructor will
5401  * not be usable for a while.
5402  *  
5403  * <li><i>loadParams</i> - an object containing parameters to pass to the 
5404  * loader callback function when locale data is missing. The parameters are not
5405  * interpretted or modified in any way. They are simply passed along. The object 
5406  * may contain any property/value pairs as long as the calling code is in
5407  * agreement with the loader callback function as to what those parameters mean.
5408  * </ul>
5409  * 
5410  * The options object is also passed down to the date constructor, and 
5411  * thus can contain the the properties as the date object being instantiated.
5412  * See the documentation for {@link GregorianDate}, and other
5413  * subclasses for more details on other parameter that may be passed in.<p>
5414  * 
5415  * Please note that if you do not give the type parameter, this factory
5416  * method will create a date object that is appropriate for the calendar
5417  * that is most commonly used in the specified or current ilib locale. 
5418  * For example, in Thailand, the most common calendar is the Thai solar 
5419  * calendar. If the current locale is "th-TH" (Thai for Thailand) and you 
5420  * use this factory method to construct a new date without specifying the
5421  * type, it will automatically give you back an instance of 
5422  * {@link ThaiSolarDate}. This is convenient because you do not 
5423  * need to know which locales use which types of dates. In fact, you 
5424  * should always use this factory method to make new date instances unless
5425  * you know that you specifically need a date in a particular calendar.<p>
5426  * 
5427  * Also note that when you pass in the date components such as year, month,
5428  * day, etc., these components should be appropriate for the given date
5429  * being instantiated. That is, in our Thai example in the previous
5430  * paragraph, the year and such should be given as a Thai solar year, not
5431  * the Gregorian year that you get from the Javascript Date class. In
5432  * order to initialize a date instance when you don't know what subclass
5433  * will be instantiated for the locale, use a parameter such as "unixtime" 
5434  * or "julianday" which are unambiguous and based on UTC time, instead of
5435  * the year/month/date date components. The date components for that UTC 
5436  * time will be calculated and the time zone offset will be automatically 
5437  * factored in.
5438  * 
5439  * @static
5440  * @param {Object=} options options controlling the construction of this instance, or
5441  * undefined to use the default options
5442  * @return {IDate} an instance of a calendar object of the appropriate type 
5443  */
5444 var DateFactory = function(options) {
5445 	var locale,
5446 		type,
5447 		sync = true,
5448 		obj;
5449 
5450 	if (options) {
5451 		if (options.locale) {
5452 			locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
5453 		}
5454 		
5455 		type = options.type || options.calendar;
5456 		
5457 		if (typeof(options.sync) === 'boolean') {
5458 			sync = options.sync;
5459 		}
5460 	}
5461 	
5462 	if (!locale) {
5463 		locale = new Locale();	// default locale
5464 	}
5465 
5466 	if (!type) {
5467 		new LocaleInfo(locale, {
5468 			sync: sync,
5469 			loadParams: options && options.loadParams,
5470 			onLoad: function(info) {
5471 				type = info.getCalendar();
5472 				
5473 				obj = DateFactory._init(type, options);
5474 			}
5475 		});
5476 	} else {
5477 		obj = DateFactory._init(type, options);
5478 	}
5479 	
5480 	return obj
5481 };
5482 
5483 /**
5484  * Map calendar names to classes to initialize in the dynamic code model.
5485  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
5486  * @private
5487  */
5488 DateFactory._dynMap = {
5489 	"coptic":       "Coptic",
5490 	"ethiopic":     "Ethiopic",
5491 	"gregorian":    "Gregorian",
5492 	"han":          "Han",
5493 	"hebrew":       "Hebrew",
5494 	"islamic":      "Islamic",
5495 	"julian":       "Julian",
5496 	"persian":      "Persian",
5497 	"persian-algo": "PersianAlgo",
5498 	"thaisolar":    "ThaiSolar"
5499 };
5500 
5501 /**
5502  * Dynamically load the code for a calendar and calendar class if necessary.
5503  * @protected
5504  */
5505 DateFactory._dynLoadDate = function (name) {
5506 	if (!IDate._constructors[name]) {
5507 		var entry = DateFactory._dynMap[name];
5508 		if (entry) {
5509 			IDate._constructors[name] = require("./" + entry + "Date.js");
5510 		}
5511 	}
5512 	return IDate._constructors[name];
5513 };
5514 
5515 /** 
5516  * @protected
5517  * @static 
5518  */
5519 DateFactory._init = function(type, options) {
5520 	var cons;
5521 	
5522 	if (ilib.isDynCode()) {
5523 		DateFactory._dynLoadDate(type);
5524 		CalendarFactory._dynLoadCalendar(type);
5525 	}
5526 	
5527 	cons = IDate._constructors[type];
5528 	
5529 	// pass the same options through to the constructor so the subclass
5530 	// has the ability to do something with if it needs to
5531 	if (!cons && typeof(options.onLoad) === "function") {
5532 	    options.onLoad(undefined);
5533 	}
5534 	return cons && new cons(options);
5535 };
5536 
5537 /**
5538  * Convert JavaScript Date objects and other types into native Dates. This accepts any
5539  * string or number that can be translated by the JavaScript Date class,
5540  * (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse)
5541  * any JavaScript Date classed object, any IDate subclass, an JulianDay object, an object
5542  * containing the normal options to initialize an IDate instance, or null (will 
5543  * return null or undefined if input is null or undefined). Normal output is 
5544  * a standard native subclass of the IDate object as appropriate for the locale.
5545  * 
5546  * @static
5547  * @protected
5548  * @param {IDate|Object|JulianDay|Date|string|number=} inDate The input date object, string or Number.
5549  * @param {IString|string=} timezone timezone to use if a new date object is created
5550  * @param {Locale|string=} locale locale to use when constructing an IDate
5551  * @return {IDate|null|undefined} an IDate subclass equivalent to the given inDate
5552  */
5553 DateFactory._dateToIlib = function(inDate, timezone, locale) {
5554 	if (typeof(inDate) === 'undefined' || inDate === null) {
5555 		return inDate;
5556 	}
5557 	if (inDate instanceof IDate) {
5558 		return inDate;
5559 	}
5560 	if (typeof(inDate) === 'number') {
5561 		return DateFactory({
5562 			unixtime: inDate,
5563 			timezone: timezone,
5564 			locale: locale
5565 		});
5566 	}
5567 	if (typeof(inDate) === 'string') {
5568 		inDate = new Date(inDate);
5569 	}
5570 	if (JSUtils.isDate(inDate)) {
5571 		return DateFactory({
5572 			unixtime: inDate.getTime(),
5573 			timezone: timezone,
5574 			locale: locale
5575 		});
5576 	}
5577 	if (inDate instanceof JulianDay) {
5578 		return DateFactory({
5579 			jd: inDate,
5580 			timezone: timezone,
5581 			locale: locale
5582 		});
5583 	}
5584 	if (typeof(inDate) === 'object') {
5585 		return DateFactory(inDate);
5586 	}
5587 	return DateFactory({
5588 		unixtime: inDate.getTime(),
5589 		timezone: timezone,
5590 		locale: locale
5591 	});
5592 };
5593 
5594 
5595 
5596 /*< ResBundle.js */
5597 /*
5598  * ResBundle.js - Resource bundle definition
5599  * 
5600  * Copyright © 2012-2016, JEDLSoft
5601  *
5602  * Licensed under the Apache License, Version 2.0 (the "License");
5603  * you may not use this file except in compliance with the License.
5604  * You may obtain a copy of the License at
5605  *
5606  *     http://www.apache.org/licenses/LICENSE-2.0
5607  *
5608  * Unless required by applicable law or agreed to in writing, software
5609  * distributed under the License is distributed on an "AS IS" BASIS,
5610  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5611  *
5612  * See the License for the specific language governing permissions and
5613  * limitations under the License.
5614  */
5615 
5616 // !depends ilib.js Locale.js LocaleInfo.js IString.js Utils.js JSUtils.js
5617 
5618 // !data pseudomap
5619 
5620 
5621 
5622 
5623 /**
5624  * @class
5625  * Create a new resource bundle instance. The resource bundle loads strings
5626  * appropriate for a particular locale and provides them via the getString 
5627  * method.<p>
5628  * 
5629  * The options object may contain any (or none) of the following properties:
5630  * 
5631  * <ul>
5632  * <li><i>locale</i> - The locale of the strings to load. If not specified, the default
5633  * locale is the the default for the web page or app in which the bundle is 
5634  * being loaded.
5635  * 
5636  * <li><i>name</i> - Base name of the resource bundle to load. If not specified the default
5637  * base name is "resources".
5638  * 
5639  * <li><i>type</i> - Name the type of strings this bundle contains. Valid values are 
5640  * "xml", "html", "text", "c", or "raw". The default is "text". If the type is "xml" or "html",
5641  * then XML/HTML entities and tags are not pseudo-translated. During a real translation, 
5642  * HTML character entities are translated to their corresponding characters in a source
5643  * string before looking that string up in the translations. Also, the characters "<", ">",
5644  * and "&" are converted to entities again in the output, but characters are left as they
5645  * are. If the type is "xml", "html", or "text" types, then the replacement parameter names
5646  * are not pseudo-translated as well so that the output can be used for formatting with 
5647  * the IString class. If the type is "c" then all C language style printf replacement
5648  * parameters (eg. "%s" and "%d") are skipped automatically. If the type is raw, all characters 
5649  * are pseudo-translated, including replacement parameters as well as XML/HTML tags and entities.
5650  * 
5651  * <li><i>lengthen</i> - when pseudo-translating the string, tell whether or not to 
5652  * automatically lengthen the string to simulate "long" languages such as German
5653  * or French. This is a boolean value. Default is false.
5654  * 
5655  * <li><i>missing</i> - what to do when a resource is missing. The choices are:
5656  * <ul>
5657  *   <li><i>source</i> - return the source string unchanged
5658  *   <li><i>pseudo</i> - return the pseudo-translated source string, translated to the
5659  *   script of the locale if the mapping is available, or just the default Latin 
5660  *   pseudo-translation if not
5661  *   <li><i>empty</i> - return the empty string 
5662  * </ul>
5663  * The default behaviour is the same as before, which is to return the source string
5664  * unchanged.
5665  * 
5666  * <li><i>onLoad</i> - a callback function to call when the resources are fully 
5667  * loaded. When the onLoad option is given, this class will attempt to
5668  * load any missing locale data using the ilib loader callback.
5669  * When the constructor is done (even if the data is already preassembled), the 
5670  * onLoad function is called with the current instance as a parameter, so this
5671  * callback can be used with preassembled or dynamic loading or a mix of the two. 
5672  * 
5673  * <li>sync - tell whether to load any missing locale data synchronously or 
5674  * asynchronously. If this option is given as "false", then the "onLoad"
5675  * callback must be given, as the instance returned from this constructor will
5676  * not be usable for a while. 
5677  *
5678  * <li><i>loadParams</i> - an object containing parameters to pass to the 
5679  * loader callback function when locale data is missing. The parameters are not
5680  * interpretted or modified in any way. They are simply passed along. The object 
5681  * may contain any property/value pairs as long as the calling code is in
5682  * agreement with the loader callback function as to what those parameters mean.
5683  * </ul>
5684  * 
5685  * The locale option may be given as a locale spec string or as an 
5686  * Locale object. If the locale option is not specified, then strings for
5687  * the default locale will be loaded.<p> 
5688  * 
5689  * The name option can be used to put groups of strings together in a
5690  * single bundle. The strings will then appear together in a JS object in
5691  * a JS file that can be included before the ilib.<p>
5692  * 
5693  * A resource bundle with a particular name is actually a set of bundles
5694  * that are each specific to a language, a language plus a region, etc. 
5695  * All bundles with the same base name should
5696  * contain the same set of source strings, but with different translations for 
5697  * the given locale. The user of the bundle does not need to be aware of 
5698  * the locale of the bundle, as long as it contains values for the strings 
5699  * it needs.<p>
5700  * 
5701  * Strings in bundles for a particular locale are inherited from parent bundles
5702  * that are more generic. In general, the hierarchy is as follows (from 
5703  * least locale-specific to most locale-specific):
5704  * 
5705  * <ol>
5706  * <li> language
5707  * <li> region
5708  * <li> language_script
5709  * <li> language_region
5710  * <li> region_variant
5711  * <li> language_script_region
5712  * <li> language_region_variant
5713  * <li> language_script_region_variant
5714  * </ol>
5715  * 
5716  * That is, if the translation for a string does not exist in the current
5717  * locale, the more-generic parent locale is searched for the string. In the
5718  * worst case scenario, the string is not found in the base locale's strings. 
5719  * In this case, the missing option guides this class on what to do. If
5720  * the missing option is "source", then the original source is returned as 
5721  * the translation. If it is "empty", the empty string is returned. If it
5722  * is "pseudo", then the pseudo-translated string that is appropriate for
5723  * the default script of the locale is returned.<p> 
5724  * 
5725  * This allows developers to create code with new or changed strings in it and check in that
5726  * code without waiting for the translations to be done first. The translated
5727  * version of the app or web site will still function properly, but will show 
5728  * a spurious untranslated string here and there until the translations are 
5729  * done and also checked in.<p>   
5730  *  
5731  * The base is whatever language your developers use to code in. For
5732  * a German web site, strings in the source code may be written in German 
5733  * for example. Often this base is English, as many web sites are coded in
5734  * English, but that is not required.<p>
5735  * 
5736  * The strings can be extracted with the ilib localization tool (which will be
5737  * shipped at some future time.) Once the strings
5738  * have been translated, the set of translated files can be generated with the
5739  * same tool. The output from the tool can be used as input to the ResBundle
5740  * object. It is up to the web page or app to make sure the JS file that defines
5741  * the bundle is included before creating the ResBundle instance.<p>
5742  * 
5743  * A special locale "zxx-XX" is used as the pseudo-translation locale because
5744  * zxx means "no linguistic information" in the ISO 639 standard, and the region 
5745  * code XX is defined to be user-defined in the ISO 3166 standard. 
5746  * Pseudo-translation is a locale where the translations are generated on
5747  * the fly based on the contents of the source string. Characters in the source 
5748  * string are replaced with other characters and returned. 
5749  * 
5750  * Example. If the source string is:
5751  * 
5752  * <pre>
5753  * "This is a string"
5754  * </pre>
5755  * 
5756  * then the pseudo-translated version might look something like this: 
5757  * 
5758  * <pre>
5759  * "Ţħïş ïş á şţřïñĝ"
5760  * </pre>
5761  * <p>
5762  * 
5763  * Pseudo-translation can be used to test that your app or web site is translatable
5764  * before an actual translation has happened. These bugs can then be fixed 
5765  * before the translation starts, avoiding an explosion of bugs later when
5766  * each language's tester registers the same bug complaining that the same 
5767  * string is not translated. When pseudo-localizing with
5768  * the Latin script, this allows the strings to be readable in the UI in the 
5769  * source language (if somewhat funky-looking), 
5770  * so that a tester can easily verify that the string is properly externalized 
5771  * and loaded from a resource bundle without the need to be able to read a
5772  * foreign language.<p> 
5773  * 
5774  * If one of a list of script tags is given in the pseudo-locale specifier, then the
5775  * pseudo-localization can map characters to very rough transliterations of
5776  * characters in the given script. For example, zxx-Hebr-XX maps strings to
5777  * Hebrew characters, which can be used to test your UI in a right-to-left
5778  * language to catch bidi bugs before a translation is done. Currently, the
5779  * list of target scripts includes Hebrew (Hebr), Chinese Simplified Han (Hans),
5780  * and Cyrillic (Cyrl) with more to be added later. If no script is explicitly
5781  * specified in the locale spec, or if the script is not supported,
5782  * then the default mapping maps Latin base characters to accented versions of
5783  * those Latin characters as in the example above.
5784  *  
5785  * When the "lengthen" property is set to true in the options, the 
5786  * pseudotranslation code will add digits to the end of the string to simulate
5787  * the lengthening that occurs when translating to other languages. The above 
5788  * example will come out like this:
5789  * 
5790  * <pre>
5791  * "Ţħïş ïş á şţřïñĝ76543210"
5792  * </pre>
5793  * 
5794  * The string is lengthened according to the length of the source string. If
5795  * the source string is less than 20 characters long, the string is lengthened 
5796  * by 50%. If the source string is 20-40 
5797  * characters long, the string is lengthened by 33%. If te string is greater
5798  * than 40 characters long, the string is lengthened by 20%.<p>
5799  * 
5800  * The pseudotranslation always ends a string with the digit "0". If you do
5801  * not see the digit "0" in the UI for your app, you know that truncation
5802  * has occurred, and the number you see at the end of the string tells you 
5803  * how many characters were truncated.<p>
5804  * 
5805  * 
5806  * @constructor
5807  * @param {?Object} options Options controlling how the bundle is created
5808  */
5809 var ResBundle = function (options) {
5810 	var lookupLocale, spec;
5811 	
5812 	this.locale = new Locale();	// use the default locale
5813 	this.baseName = "strings";
5814 	this.type = "text";
5815 	this.loadParams = {};
5816 	this.missing = "source";
5817 	this.sync = true;
5818 	
5819 	if (options) {
5820 		if (options.locale) {
5821 			this.locale = (typeof(options.locale) === 'string') ? 
5822 					new Locale(options.locale) :
5823 					options.locale;
5824 		}
5825 		if (options.name) {
5826 			this.baseName = options.name;
5827 		}
5828 		if (options.type) {
5829 			this.type = options.type;
5830 		}
5831 		this.lengthen = options.lengthen || false;
5832 		
5833 		if (typeof(options.sync) !== 'undefined') {
5834 			this.sync = !!options.sync;
5835 		}
5836 		
5837 		if (typeof(options.loadParams) !== 'undefined') {
5838 			this.loadParams = options.loadParams;
5839 		}
5840 		if (typeof(options.missing) !== 'undefined') {
5841 			if (options.missing === "pseudo" || options.missing === "empty") {
5842 				this.missing = options.missing;
5843 			}
5844 		}
5845 	} else {
5846 		options = {sync: true};
5847 	}
5848 	
5849 	this.map = {};
5850 
5851 	if (!ilib.data.cache.ResBundle) {
5852         ilib.data.cache.ResBundle = {};
5853     }
5854 	
5855 	lookupLocale = this.locale.isPseudo() ? new Locale("en-US") : this.locale;
5856 	var object = "ResBundle-" + this.baseName; 
5857 
5858 	Utils.loadData({
5859 		object: object,
5860 		locale: lookupLocale, 
5861 		name: this.baseName + ".json", 
5862 		sync: this.sync, 
5863 		loadParams: this.loadParams, 
5864 		callback: ilib.bind(this, function (map) {
5865 			if (!map) {
5866 				map = ilib.data[this.baseName] || {};
5867 				spec = lookupLocale.getSpec().replace(/-/g, '_');
5868 				ilib.data.cache[object][spec] = map;
5869 			}
5870 			this.map = map;
5871 			if (this.locale.isPseudo()) {
5872 				if (!ilib.data.cache.ResBundle.pseudomap) {
5873 				    ilib.data.cache.ResBundle.pseudomap = {};
5874 				}
5875 	
5876 				this._loadPseudo(this.locale, options.onLoad);
5877 			} else if (this.missing === "pseudo") {
5878 				if (!ilib.data.cache.ResBundle.pseudomap) {
5879 				    ilib.data.cache.ResBundle.pseudomap = {};
5880 				}
5881 	
5882 				new LocaleInfo(this.locale, {
5883 					sync: this.sync,
5884 					loadParams: this.loadParams,
5885 					onLoad: ilib.bind(this, function (li) {
5886 						var pseudoLocale = new Locale("zxx", "XX", undefined, li.getDefaultScript());
5887 						this._loadPseudo(pseudoLocale, options.onLoad);
5888 					})
5889 				});
5890 			} else {
5891 				if (typeof(options.onLoad) === 'function') {
5892 					options.onLoad(this);
5893 				}
5894 			}
5895 		})
5896 	});
5897 
5898 	// console.log("Merged resources " + this.locale.toString() + " are: " + JSON.stringify(this.map));
5899 	//if (!this.locale.isPseudo() && JSUtils.isEmpty(this.map)) {
5900 	//	console.log("Resources for bundle " + this.baseName + " locale " + this.locale.toString() + " are not available.");
5901 	//}
5902 };
5903 
5904 ResBundle.defaultPseudo = ilib.data.pseudomap || {
5905 	"a": "à",
5906 	"e": "ë",
5907 	"i": "í",
5908 	"o": "õ",
5909 	"u": "ü",
5910 	"y": "ÿ",
5911 	"A": "Ã",
5912 	"E": "Ë",
5913 	"I": "Ï",
5914 	"O": "Ø",
5915 	"U": "Ú",
5916 	"Y": "Ŷ"
5917 };
5918 
5919 ResBundle.prototype = {
5920     /**
5921      * @protected
5922      */
5923     _loadPseudo: function (pseudoLocale, onLoad) {
5924 		Utils.loadData({
5925 			object: "ResBundle", 
5926 			locale: pseudoLocale, 
5927 			name: "pseudomap.json", 
5928 			sync: this.sync, 
5929 			loadParams: this.loadParams, 
5930 			callback: ilib.bind(this, function (map) {
5931 				if (!map || JSUtils.isEmpty(map)) {
5932 					map = ResBundle.defaultPseudo;
5933 					var spec = pseudoLocale.getSpec().replace(/-/g, '_');
5934 					ilib.data.cache.ResBundle.pseudomap[spec] = map;
5935 				}
5936 				this.pseudomap = map;
5937 				if (typeof(onLoad) === 'function') {
5938 					onLoad(this);
5939 				}	
5940 			})
5941 		});
5942     },
5943     
5944 	/**
5945 	 * Return the locale of this resource bundle.
5946 	 * @return {Locale} the locale of this resource bundle object 
5947 	 */
5948 	getLocale: function () {
5949 		return this.locale;
5950 	},
5951 	
5952 	/**
5953 	 * Return the name of this resource bundle. This corresponds to the name option
5954 	 * given to the constructor.
5955 	 * @return {string} name of the the current instance
5956 	 */
5957 	getName: function () {
5958 		return this.baseName;
5959 	},
5960 	
5961 	/**
5962 	 * Return the type of this resource bundle. This corresponds to the type option
5963 	 * given to the constructor.
5964 	 * @return {string} type of the the current instance
5965 	 */
5966 	getType: function () {
5967 		return this.type;
5968 	},
5969 
5970 	percentRE: new RegExp("%(\\d+\\$)?([\\-#\\+ 0,\\(])?(\\d+)?(\\.\\d+)?[bBhHsScCdoxXeEfgGaAtT%n]"),
5971 
5972 	/**
5973 	 * @private
5974 	 * Pseudo-translate a string
5975 	 */
5976 	_pseudo: function (str) {
5977 		if (!str) {
5978 			return undefined;
5979 		}
5980 		var ret = "", i;
5981 		for (i = 0; i < str.length; i++) {
5982 			if (this.type !== "raw") {
5983 				if (this.type === "html" || this.type === "xml") {
5984 					if (str.charAt(i) === '<') {
5985 						ret += str.charAt(i++);
5986 						while (i < str.length && str.charAt(i) !== '>') {
5987 							ret += str.charAt(i++);
5988 						}
5989 					} else if (str.charAt(i) === '&') {
5990 						ret += str.charAt(i++);
5991 						while (i < str.length && str.charAt(i) !== ';' && str.charAt(i) !== ' ') {
5992 							ret += str.charAt(i++);
5993 						}
5994 					} else if (str.charAt(i) === '\\' && str.charAt(i+1) === "u") {
5995 						ret += str.substring(i, i+6);
5996 						i += 6;
5997 					}
5998 				} else if (this.type === "c") {
5999 					if (str.charAt(i) === "%") {
6000 						var m = this.percentRE.exec(str.substring(i));
6001 						if (m && m.length) {
6002 							// console.log("Match found: " + JSON.stringify(m[0].replace("%", "%%")));
6003 							ret += m[0];
6004 							i += m[0].length;
6005 						}
6006 					}
6007 
6008 				}
6009 				if (i < str.length) { 
6010 					if (str.charAt(i) === '{') {
6011 						ret += str.charAt(i++);
6012 						while (i < str.length && str.charAt(i) !== '}') {
6013 							ret += str.charAt(i++);
6014 						}
6015 						if (i < str.length) {
6016 							ret += str.charAt(i);
6017 						}
6018 					} else {
6019 						ret += this.pseudomap[str.charAt(i)] || str.charAt(i);
6020 					}
6021 				}
6022 			} else {
6023 				ret += this.pseudomap[str.charAt(i)] || str.charAt(i);
6024 			}
6025 		}
6026 		if (this.lengthen) {
6027 			var add;
6028 			if (ret.length <= 20) {
6029 				add = Math.round(ret.length / 2);
6030 			} else if (ret.length > 20 && ret.length <= 40) {
6031 				add = Math.round(ret.length / 3);
6032 			} else {
6033 				add = Math.round(ret.length / 5);
6034 			}
6035 			for (i = add-1; i >= 0; i--) {
6036 				ret += (i % 10);
6037 			}
6038 		}
6039 		if (this.locale.getScript() === "Hans" || this.locale.getScript() === "Hant" ||
6040 				this.locale.getScript() === "Hani" ||
6041 				this.locale.getScript() === "Hrkt" || this.locale.getScript() === "Jpan" ||
6042 				this.locale.getScript() === "Hira" || this.locale.getScript() === "Kana" ) {
6043 			// simulate Asian languages by getting rid of all the spaces
6044 			ret = ret.replace(/ /g, "");
6045 		}
6046 		return ret;
6047 	},
6048 	
6049 	/**
6050 	 * @private
6051 	 * Escape html characters in the output.
6052 	 */
6053 	_escapeXml: function (str) {
6054 		str = str.replace(/&/g, '&');
6055 		str = str.replace(/</g, '<');
6056 		str = str.replace(/>/g, '>');
6057 		return str;
6058 	},
6059 
6060 	/**
6061 	 * @private
6062 	 * @param {string} str the string to unescape
6063 	 */
6064 	_unescapeXml: function (str) {
6065 		str = str.replace(/&/g, '&');
6066 		str = str.replace(/</g, '<');
6067 		str = str.replace(/>/g, '>');
6068 		return str;
6069 	},
6070 	
6071 	/**
6072 	 * @private
6073 	 * Create a key name out of a source string. All this does so far is 
6074 	 * compress sequences of white space into a single space on the assumption
6075 	 * that this doesn't really change the meaning of the string, and therefore
6076 	 * all such strings that compress to the same thing should share the same
6077 	 * translation.
6078 	 * @param {null|string=} source the source string to make a key out of
6079 	 */
6080 	_makeKey: function (source) {
6081 		if (!source) return undefined;
6082 		var key = source.replace(/\s+/gm, ' ');
6083 		return (this.type === "xml" || this.type === "html") ? this._unescapeXml(key) : key;
6084 	},
6085 	
6086     /**
6087      * @private
6088      */
6089 	_getStringSingle: function(source, key, escapeMode) {
6090         if (!source && !key) return new IString("");
6091 
6092         var trans;
6093         if (this.locale.isPseudo()) {
6094             var str = source ? source : this.map[key];
6095             trans = this._pseudo(str || key);
6096         } else {
6097             var keyName = key || this._makeKey(source);
6098             if (typeof(this.map[keyName]) !== 'undefined') {
6099                 trans = this.map[keyName];
6100             } else if (this.missing === "pseudo") {
6101                 trans = this._pseudo(source || key);
6102             } else if (this.missing === "empty") {
6103                 trans = "";
6104             } else {
6105                 trans = source;
6106             }
6107         }
6108 
6109         if (escapeMode && escapeMode !== "none") {
6110             if (escapeMode == "default") {
6111                 escapeMode = this.type;
6112             }
6113             if (escapeMode === "xml" || escapeMode === "html") {
6114                 trans = this._escapeXml(trans);
6115             } else if (escapeMode == "js" || escapeMode === "attribute") {
6116                 trans = trans.replace(/'/g, "\\\'").replace(/"/g, "\\\"");
6117             }
6118         }
6119         if (trans === undefined) {
6120             return undefined;
6121         } else {
6122             var ret = new IString(trans);
6123             ret.setLocale(this.locale.getSpec(), true, this.loadParams); // no callback
6124             return ret;
6125         }
6126 	}, 
6127 	    
6128 	/**
6129 	 * Return a localized string, array, or object. This method can localize individual
6130 	 * strings or arrays of strings.<p>
6131 	 * 
6132 	 * If the source parameter is a string, the translation of that string is looked
6133 	 * up and returned. If the source parameter is an array of strings, then the translation 
6134 	 * of each of the elements of that array is looked up, and an array of translated strings
6135 	 * is returned. <p>
6136 	 * 
6137 	 * If any string is not found in the loaded set of
6138 	 * resources, the original source string is returned. If the key is not given,
6139 	 * then the source string itself is used as the key. In the case where the 
6140 	 * source string is used as the key, the whitespace is compressed down to 1 space
6141 	 * each, and the whitespace at the beginning and end of the string is trimmed.<p>
6142 	 * 
6143 	 * The escape mode specifies what type of output you are escaping the returned
6144 	 * string for. Modes are similar to the types: 
6145 	 * 
6146 	 * <ul>
6147 	 * <li>"html" -- prevents HTML injection by escaping the characters < > and &
6148 	 * <li>"xml" -- currently same as "html" mode
6149 	 * <li>"js" -- prevents breaking Javascript syntax by backslash escaping all quote and 
6150 	 * double-quote characters
6151 	 * <li>"attribute" -- meant for HTML attribute values. Currently this is the same as
6152 	 * "js" escape mode.
6153 	 * <li>"default" -- use the type parameter from the constructor as the escape mode as well
6154 	 * <li>"none" or undefined -- no escaping at all.
6155 	 * </ul>
6156 	 * 
6157 	 * The type parameter of the constructor specifies what type of strings this bundle
6158 	 * is operating upon. This allows pseudo-translation and automatic key generation
6159 	 * to happen properly by telling this class how to parse the string. The escape mode 
6160 	 * for this method is different in that it specifies how this string will be used in 
6161 	 * the calling code and therefore how to escape it properly.<p> 
6162 	 * 
6163 	 * For example, a section of Javascript code may be constructing an HTML snippet in a 
6164 	 * string to add to the web page. In this case, the type parameter in the constructor should
6165 	 * be "html" so that the source string can be parsed properly, but the escape mode should
6166 	 * be "js" so that the output string can be used in Javascript without causing syntax
6167 	 * errors.
6168 	 * 
6169 	 * @param {?string|Array.<string>=} source the source string or strings to translate
6170 	 * @param {?string|Array.<string>=} key optional name of the key, if any
6171 	 * @param {?string=} escapeMode escape mode, if any
6172 	 * @return {IString|Array.<IString>|undefined} the translation of the given source/key or undefined 
6173 	 * if the translation is not found and the source is undefined 
6174 	 */
6175 	getString: function (source, key, escapeMode) {
6176 		if (!source && !key) return new IString("");
6177 		
6178 		//if (typeof(source) === "object") {
6179             // TODO localize objects
6180         //} else
6181 		
6182         if (ilib.isArray(source)) {
6183 		    return source.map(ilib.bind(this, function(str) {
6184 		       return typeof(str) === "string" ? this._getStringSingle(str, key, escapeMode) : str;
6185 		    }));
6186 		} else {
6187             return this._getStringSingle(source, key, escapeMode);
6188 		}
6189 	},
6190 	
6191 	/**
6192 	 * Return a localized string as an intrinsic Javascript String object. This does the same thing as
6193 	 * the getString() method, but it returns a regular Javascript string instead of
6194 	 * and IString instance. This means it cannot be formatted with the format()
6195 	 * method without being wrapped in an IString instance first.
6196 	 * 
6197 	 * @param {?string|Array.<string>=} source the source string to translate
6198 	 * @param {?string|Array.<string>=} key optional name of the key, if any
6199 	 * @param {?string=} escapeMode escape mode, if any
6200 	 * @return {string|Array.<string>|undefined} the translation of the given source/key or undefined 
6201 	 * if the translation is not found and the source is undefined
6202 	 */
6203 	getStringJS: function(source, key, escapeMode) {
6204 		if (typeof(source) === 'undefined' && typeof(key) === 'undefined') {
6205 			return undefined;
6206 		}
6207 		//if (typeof(source) === "object") {
6208 		    // TODO localize objects
6209 		//} else 
6210 		
6211 		if (ilib.isArray(source)) {
6212 		    return this.getString(source, key, escapeMode).map(function(str) {
6213 		       return (str && str instanceof IString) ? str.toString() : str;
6214 		    });
6215 		} else {
6216             var s = this.getString(source, key, escapeMode); 
6217             return s ? s.toString() : undefined;
6218 		}
6219 	},
6220 	
6221 	/**
6222 	 * Return true if the current bundle contains a translation for the given key and
6223 	 * source. The
6224 	 * getString method will always return a string for any given key and source 
6225 	 * combination, so it cannot be used to tell if a translation exists. Either one
6226 	 * or both of the source and key must be specified. If both are not specified,
6227 	 * this method will return false.
6228 	 * 
6229 	 * @param {?string=} source source string to look up
6230 	 * @param {?string=} key key to look up
6231 	 * @return {boolean} true if this bundle contains a translation for the key, and 
6232 	 * false otherwise
6233 	 */
6234 	containsKey: function(source, key) {
6235 		if (typeof(source) === 'undefined' && typeof(key) === 'undefined') {
6236 			return false;
6237 		}
6238 		
6239 		var keyName = key || this._makeKey(source);
6240 		return typeof(this.map[keyName]) !== 'undefined';
6241 	},
6242 	
6243 	/**
6244 	 * Return the merged resources as an entire object. When loading resources for a
6245 	 * locale that are not just a set of translated strings, but instead an entire 
6246 	 * structured javascript object, you can gain access to that object via this call. This method
6247 	 * will ensure that all the of the parts of the object are correct for the locale.<p>
6248 	 * 
6249 	 * For pre-assembled data, it starts by loading <i>ilib.data[name]</i>, where 
6250 	 * <i>name</i> is the base name for this set of resources. Then, it successively 
6251 	 * merges objects in the base data using progressively more locale-specific data. 
6252 	 * It loads it in this order from <i>ilib.data</i>:
6253 	 * 
6254 	 * <ol>
6255 	 * <li> language
6256 	 * <li> region
6257 	 * <li> language_script
6258 	 * <li> language_region
6259 	 * <li> region_variant
6260 	 * <li> language_script_region
6261 	 * <li> language_region_variant
6262 	 * <li> language_script_region_variant
6263 	 * </ol>
6264 	 * 
6265 	 * For dynamically loaded data, the code attempts to load the same sequence as
6266 	 * above, but with slash path separators instead of underscores.<p>
6267 	 *  
6268 	 * Loading the resources this way allows the program to share resources between all
6269 	 * locales that share a common language, region, or script. As a 
6270 	 * general rule-of-thumb, resources should be as generic as possible in order to
6271 	 * cover as many locales as possible.
6272 	 * 
6273 	 * @return {Object} returns the object that is the basis for this resources instance
6274 	 */
6275 	getResObj: function () {
6276 		return this.map;
6277 	}
6278 };
6279 
6280 
6281 
6282 /*< GregorianCal.js */
6283 /*
6284  * gregorian.js - Represent a Gregorian calendar object.
6285  * 
6286  * Copyright © 2012-2015,2018, JEDLSoft
6287  *
6288  * Licensed under the Apache License, Version 2.0 (the "License");
6289  * you may not use this file except in compliance with the License.
6290  * You may obtain a copy of the License at
6291  *
6292  *     http://www.apache.org/licenses/LICENSE-2.0
6293  *
6294  * Unless required by applicable law or agreed to in writing, software
6295  * distributed under the License is distributed on an "AS IS" BASIS,
6296  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6297  *
6298  * See the License for the specific language governing permissions and
6299  * limitations under the License.
6300  */
6301 
6302 
6303 /* !depends Calendar.js MathUtils.js */
6304 
6305 
6306 /**
6307  * @class
6308  * Construct a new Gregorian calendar object. This class encodes information about
6309  * a Gregorian calendar.<p>
6310  * 
6311  * 
6312  * @constructor
6313  * @param {{noinstance:boolean}=} options
6314  * @extends Calendar
6315  */
6316 var GregorianCal = function(options) {
6317 	if (!options || !options.noinstance) {
6318 		this.type = "gregorian";
6319 	}
6320 	
6321 	if (options && typeof(options.onLoad) === "function") {
6322 	    options.onLoad(this);
6323 	}
6324 };
6325 
6326 /**
6327  * the lengths of each month 
6328  * @private
6329  * @const
6330  * @type Array.<number> 
6331  */
6332 GregorianCal.monthLengths = [
6333 	31,  /* Jan */
6334 	28,  /* Feb */
6335 	31,  /* Mar */
6336 	30,  /* Apr */
6337 	31,  /* May */
6338 	30,  /* Jun */
6339 	31,  /* Jul */
6340 	31,  /* Aug */
6341 	30,  /* Sep */
6342 	31,  /* Oct */
6343 	30,  /* Nov */
6344 	31   /* Dec */
6345 ];
6346 
6347 /**
6348  * Return the number of months in the given year. The number of months in a year varies
6349  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
6350  * days in a year to an entire solar year. The month is represented as a 1-based number
6351  * where 1=first month, 2=second month, etc.
6352  * 
6353  * @param {number} year a year for which the number of months is sought
6354  * @return {number} The number of months in the given year
6355  */
6356 GregorianCal.prototype.getNumMonths = function(year) {
6357 	return 12;
6358 };
6359 
6360 /**
6361  * Return the number of days in a particular month in a particular year. This function
6362  * can return a different number for a month depending on the year because of things
6363  * like leap years.
6364  * 
6365  * @param {number} month the month for which the length is sought
6366  * @param {number} year the year within which that month can be found
6367  * @return {number} the number of days within the given month in the given year
6368  */
6369 GregorianCal.prototype.getMonLength = function(month, year) {
6370 	if (month !== 2 || !this.isLeapYear(year)) {
6371 		return GregorianCal.monthLengths[month-1];
6372 	} else {
6373 		return 29;
6374 	}
6375 };
6376 
6377 /**
6378  * Return true if the given year is a leap year in the Gregorian calendar.
6379  * The year parameter may be given as a number, or as a GregDate object.
6380  * @param {number|GregorianDate} year the year for which the leap year information is being sought
6381  * @return {boolean} true if the given year is a leap year
6382  */
6383 GregorianCal.prototype.isLeapYear = function(year) {
6384 	var y = (typeof(year) === 'number' ? year : year.getYears());
6385 	var centuries = MathUtils.mod(y, 400);
6386 	return (MathUtils.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300);
6387 };
6388 
6389 /**
6390  * Return the type of this calendar.
6391  * 
6392  * @return {string} the name of the type of this calendar 
6393  */
6394 GregorianCal.prototype.getType = function() {
6395 	return this.type;
6396 };
6397 
6398 /* register this calendar for the factory method */
6399 Calendar._constructors["gregorian"] = GregorianCal;
6400 
6401 
6402 
6403 /*< RataDie.js */
6404 /*
6405  * ratadie.js - Represent the RD date number in the calendar
6406  * 
6407  * Copyright © 2014-2015, JEDLSoft
6408  *
6409  * Licensed under the Apache License, Version 2.0 (the "License");
6410  * you may not use this file except in compliance with the License.
6411  * You may obtain a copy of the License at
6412  *
6413  *     http://www.apache.org/licenses/LICENSE-2.0
6414  *
6415  * Unless required by applicable law or agreed to in writing, software
6416  * distributed under the License is distributed on an "AS IS" BASIS,
6417  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6418  *
6419  * See the License for the specific language governing permissions and
6420  * limitations under the License.
6421  */
6422 
6423 /* !depends 
6424 JulianDay.js
6425 MathUtils.js
6426 JSUtils.js
6427 */
6428 
6429 
6430 /**
6431  * @class
6432  * Construct a new RD date number object. The constructor parameters can 
6433  * contain any of the following properties:
6434  * 
6435  * <ul>
6436  * <li><i>unixtime<i> - sets the time of this instance according to the given 
6437  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
6438  * 
6439  * <li><i>julianday</i> - sets the time of this instance according to the given
6440  * Julian Day instance or the Julian Day given as a float
6441  * 
6442  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
6443  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
6444  * linear count of years since the beginning of the epoch, much like other calendars. This linear
6445  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
6446  * to 60 and treated as if it were a year in the regular 60-year cycle.
6447  * 
6448  * <li><i>year</i> - any integer, including 0
6449  * 
6450  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
6451  * 
6452  * <li><i>day</i> - 1 to 31
6453  * 
6454  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
6455  * is always done with an unambiguous 24 hour representation
6456  * 
6457  * <li><i>minute</i> - 0 to 59
6458  * 
6459  * <li><i>second</i> - 0 to 59
6460  * 
6461  * <li><i>millisecond</i> - 0 to 999
6462  * 
6463  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 
6464  * the parts or specify the minutes, seconds, and milliseconds, but not both. This is only used
6465  * in the Hebrew calendar. 
6466  * 
6467  * <li><i>minute</i> - 0 to 59
6468  * 
6469  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
6470  * </ul>
6471  *
6472  * If the constructor is called with another date instance instead of
6473  * a parameter block, the other instance acts as a parameter block and its
6474  * settings are copied into the current instance.<p>
6475  * 
6476  * If the constructor is called with no arguments at all or if none of the 
6477  * properties listed above are present, then the RD is calculate based on 
6478  * the current date at the time of instantiation. <p>
6479  * 
6480  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
6481  * specified in the params, it is assumed that they have the smallest possible
6482  * value in the range for the property (zero or one).<p>
6483  * 
6484  * 
6485  * @private
6486  * @constructor
6487  * @param {Object=} params parameters that govern the settings and behaviour of this RD date
6488  */
6489 var RataDie = function(params) {
6490 	if (params) {
6491 		if (typeof(params.date) !== 'undefined') {
6492 			// accept JS Date classes or strings
6493 			var date = params.date;
6494 			if (!(JSUtils.isDate(date))) {
6495 				date = new Date(date); // maybe a string initializer?
6496 			}
6497 			this._setTime(date.getTime());
6498 		} else if (typeof(params.unixtime) !== 'undefined') {
6499 			this._setTime(parseInt(params.unixtime, 10));
6500 		} else if (typeof(params.julianday) !== 'undefined') {
6501 			// JD time is defined to be UTC
6502 			this._setJulianDay(parseFloat(params.julianday));
6503 		} else if (params.year || params.month || params.day || params.hour ||
6504 				params.minute || params.second || params.millisecond || params.parts || params.cycle) {
6505 			this._setDateComponents(params);
6506 		} else if (typeof(params.rd) !== 'undefined') {
6507 			/**
6508 			 * @type {number} the Rata Die number of this date for this calendar type
6509 			 */
6510 			this.rd = (typeof(params.rd) === 'object' && params.rd instanceof RataDie) ? params.rd.rd : params.rd;
6511 		}
6512 	}
6513 	
6514 	if (typeof(this.rd) === 'undefined' || isNaN(this.rd)) {
6515 		var now = new Date();
6516 		this._setTime(now.getTime());
6517 	}
6518 };
6519 
6520 /**
6521  * @private
6522  * @const
6523  * @type {number}
6524  */
6525 RataDie.gregorianEpoch = 1721424.5;
6526 
6527 RataDie.prototype = {
6528 	/**
6529 	 * @protected
6530 	 * @type {number}
6531 	 * the difference between a zero Julian day and the zero Gregorian date. 
6532 	 */
6533 	epoch: RataDie.gregorianEpoch,
6534 	
6535 	/**
6536 	 * Set the RD of this instance according to the given unix time. Unix time is
6537 	 * the number of milliseconds since midnight on Jan 1, 1970.
6538 	 *
6539 	 * @protected
6540 	 * @param {number} millis the unix time to set this date to in milliseconds 
6541 	 */
6542 	_setTime: function(millis) {
6543 		// 2440587.5 is the julian day of midnight Jan 1, 1970, UTC (Gregorian)
6544 		this._setJulianDay(2440587.5 + millis / 86400000);
6545 	},
6546 
6547 	/**
6548 	 * Set the date of this instance using a Julian Day.
6549 	 * @protected
6550 	 * @param {number} date the Julian Day to use to set this date
6551 	 */
6552 	_setJulianDay: function (date) {
6553 		var jd = (typeof(date) === 'number') ? new JulianDay(date) : date;
6554 		// round to the nearest millisecond
6555 		this.rd = MathUtils.halfup((jd.getDate() - this.epoch) * 86400000) / 86400000;
6556 	},
6557 
6558 	/**
6559 	 * Return the rd number of the particular day of the week on or before the 
6560 	 * given rd. eg. The Sunday on or before the given rd.
6561 	 * @protected
6562 	 * @param {number} rd the rata die date of the reference date
6563 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
6564 	 * to the current date
6565 	 * @return {number} the rd of the day of the week
6566 	 */
6567 	_onOrBefore: function(rd, dayOfWeek) {
6568 		return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 2, 7);
6569 	},
6570 	
6571 	/**
6572 	 * Return the rd number of the particular day of the week on or before the current rd.
6573 	 * eg. The Sunday on or before the current rd. If the offset is given, the calculation
6574 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
6575 	 * wall time, so it it would give the wrong day of the week if this calculation was
6576 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
6577 	 * may be done in wall time, the return value is nonetheless always given in UTC.
6578 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
6579 	 * to the current date
6580 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
6581 	 * not given
6582 	 * @return {number} the rd of the day of the week
6583 	 */
6584 	onOrBefore: function(dayOfWeek, offset) {
6585 		offset = offset || 0;
6586 		return this._onOrBefore(this.rd + offset, dayOfWeek) - offset;
6587 	},
6588 	
6589 	/**
6590 	 * Return the rd number of the particular day of the week on or before the current rd.
6591 	 * eg. The Sunday on or before the current rd. If the offset is given, the calculation
6592 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
6593 	 * wall time, so it it would give the wrong day of the week if this calculation was
6594 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
6595 	 * may be done in wall time, the return value is nonetheless always given in UTC.
6596 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
6597 	 * to the reference date
6598 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
6599 	 * not given
6600 	 * @return {number} the day of the week
6601 	 */
6602 	onOrAfter: function(dayOfWeek, offset) {
6603 		offset = offset || 0;
6604 		return this._onOrBefore(this.rd+6+offset, dayOfWeek) - offset;
6605 	},
6606 	
6607 	/**
6608 	 * Return the rd number of the particular day of the week before the current rd.
6609 	 * eg. The Sunday before the current rd. If the offset is given, the calculation
6610 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
6611 	 * wall time, so it it would give the wrong day of the week if this calculation was
6612 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
6613 	 * may be done in wall time, the return value is nonetheless always given in UTC.
6614 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
6615 	 * to the reference date
6616 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
6617 	 * not given
6618 	 * @return {number} the day of the week
6619 	 */
6620 	before: function(dayOfWeek, offset) {
6621 		offset = offset || 0;
6622 		return this._onOrBefore(this.rd-1+offset, dayOfWeek) - offset;
6623 	},
6624 	
6625 	/**
6626 	 * Return the rd number of the particular day of the week after the current rd.
6627 	 * eg. The Sunday after the current rd. If the offset is given, the calculation
6628 	 * happens in wall time instead of UTC. UTC time may be a day before or day behind 
6629 	 * wall time, so it it would give the wrong day of the week if this calculation was
6630 	 * done in UTC time when the caller really wanted wall time. Even though the calculation
6631 	 * may be done in wall time, the return value is nonetheless always given in UTC.
6632 	 * @param {number} dayOfWeek the day of the week that is being sought relative 
6633 	 * to the reference date
6634 	 * @param {number=} offset RD offset for the time zone. Zero is assumed if this param is
6635 	 * not given
6636 	 * @return {number} the day of the week
6637 	 */
6638 	after: function(dayOfWeek, offset) {
6639 		offset = offset || 0;
6640 		return this._onOrBefore(this.rd+7+offset, dayOfWeek) - offset;
6641 	},
6642 
6643 	/**
6644 	 * Return the unix time equivalent to this Gregorian date instance. Unix time is
6645 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. This method only
6646 	 * returns a valid number for dates between midnight, Jan 1, 1970 and  
6647 	 * Jan 19, 2038 at 3:14:07am when the unix time runs out. If this instance 
6648 	 * encodes a date outside of that range, this method will return -1.
6649 	 * 
6650 	 * @return {number} a number giving the unix time, or -1 if the date is outside the
6651 	 * valid unix time range
6652 	 */
6653 	getTime: function() {
6654 		// earlier than Jan 1, 1970
6655 		// or later than Jan 19, 2038 at 3:14:07am
6656 		var jd = this.getJulianDay();
6657 		if (jd < 2440587.5 || jd > 2465442.634803241) { 
6658 			return -1;
6659 		}
6660 	
6661 		// avoid the rounding errors in the floating point math by only using
6662 		// the whole days from the rd, and then calculating the milliseconds directly
6663 		return Math.round((jd - 2440587.5) * 86400000);
6664 	},
6665 
6666 	/**
6667 	 * Return the extended unix time equivalent to this Gregorian date instance. Unix time is
6668 	 * the number of milliseconds since midnight on Jan 1, 1970 UTC. Traditionally unix time
6669 	 * (or the type "time_t" in C/C++) is only encoded with a unsigned 32 bit integer, and thus 
6670 	 * runs out on Jan 19, 2038. However, most Javascript engines encode numbers well above 
6671 	 * 32 bits and the Date object allows you to encode up to 100 million days worth of time 
6672 	 * after Jan 1, 1970, and even more interestingly 100 million days worth of time before
6673 	 * Jan 1, 1970 as well. This method returns the number of milliseconds in that extended 
6674 	 * range. If this instance encodes a date outside of that range, this method will return
6675 	 * NaN.
6676 	 * 
6677 	 * @return {number} a number giving the extended unix time, or NaN if the date is outside 
6678 	 * the valid extended unix time range
6679 	 */
6680 	getTimeExtended: function() {
6681 		var jd = this.getJulianDay();
6682 		
6683 		// test if earlier than Jan 1, 1970 - 100 million days
6684 		// or later than Jan 1, 1970 + 100 million days
6685 		if (jd < -97559412.5 || jd > 102440587.5) { 
6686 			return NaN;
6687 		}
6688 	
6689 		// avoid the rounding errors in the floating point math by only using
6690 		// the whole days from the rd, and then calculating the milliseconds directly
6691 		return Math.round((jd - 2440587.5) * 86400000);
6692 	},
6693 
6694 	/**
6695 	 * Return the Julian Day equivalent to this calendar date as a number.
6696 	 * This returns the julian day in UTC.
6697 	 * 
6698 	 * @return {number} the julian date equivalent of this date
6699 	 */
6700 	getJulianDay: function() {
6701 		return this.rd + this.epoch;
6702 	},
6703 
6704 	/**
6705 	 * Return the Rata Die (fixed day) number of this RD date.
6706 	 * 
6707 	 * @return {number} the rd date as a number
6708 	 */
6709 	getRataDie: function() {
6710 		return this.rd;
6711 	}
6712 };
6713 
6714 
6715 /*< GregRataDie.js */
6716 /*
6717  * GregRataDie.js - Represent the RD date number in the Gregorian calendar
6718  * 
6719  * Copyright © 2014-2015, JEDLSoft
6720  *
6721  * Licensed under the Apache License, Version 2.0 (the "License");
6722  * you may not use this file except in compliance with the License.
6723  * You may obtain a copy of the License at
6724  *
6725  *     http://www.apache.org/licenses/LICENSE-2.0
6726  *
6727  * Unless required by applicable law or agreed to in writing, software
6728  * distributed under the License is distributed on an "AS IS" BASIS,
6729  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6730  *
6731  * See the License for the specific language governing permissions and
6732  * limitations under the License.
6733  */
6734 
6735 /* !depends 
6736 GregorianCal.js
6737 RataDie.js
6738 MathUtils.js
6739 */
6740 
6741 
6742 /**
6743  * @class
6744  * Construct a new Gregorian RD date number object. The constructor parameters can 
6745  * contain any of the following properties:
6746  * 
6747  * <ul>
6748  * <li><i>unixtime<i> - sets the time of this instance according to the given 
6749  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
6750  * 
6751  * <li><i>julianday</i> - sets the time of this instance according to the given
6752  * Julian Day instance or the Julian Day given as a float
6753  * 
6754  * <li><i>year</i> - any integer, including 0
6755  * 
6756  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
6757  * 
6758  * <li><i>day</i> - 1 to 31
6759  * 
6760  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
6761  * is always done with an unambiguous 24 hour representation
6762  * 
6763  * <li><i>minute</i> - 0 to 59
6764  * 
6765  * <li><i>second</i> - 0 to 59
6766  * 
6767  * <li><i>millisecond</i> - 0 to 999
6768  * 
6769  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
6770  * </ul>
6771  *
6772  * If the constructor is called with another Gregorian date instance instead of
6773  * a parameter block, the other instance acts as a parameter block and its
6774  * settings are copied into the current instance.<p>
6775  * 
6776  * If the constructor is called with no arguments at all or if none of the 
6777  * properties listed above are present, then the RD is calculate based on 
6778  * the current date at the time of instantiation. <p>
6779  * 
6780  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
6781  * specified in the params, it is assumed that they have the smallest possible
6782  * value in the range for the property (zero or one).<p>
6783  * 
6784  * 
6785  * @private
6786  * @constructor
6787  * @extends RataDie
6788  * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian RD date
6789  */
6790 var GregRataDie = function(params) {
6791 	this.cal = params && params.cal || new GregorianCal();
6792 	/** @type {number|undefined} */
6793 	this.rd = NaN;
6794 	RataDie.call(this, params);
6795 };
6796 
6797 GregRataDie.prototype = new RataDie();
6798 GregRataDie.prototype.parent = RataDie;
6799 GregRataDie.prototype.constructor = GregRataDie;
6800 
6801 /**
6802  * the cumulative lengths of each month, for a non-leap year 
6803  * @private
6804  * @const
6805  * @type Array.<number>
6806  */
6807 GregRataDie.cumMonthLengths = [
6808     0,   /* Jan */
6809 	31,  /* Feb */
6810 	59,  /* Mar */
6811 	90,  /* Apr */
6812 	120, /* May */
6813 	151, /* Jun */
6814 	181, /* Jul */
6815 	212, /* Aug */
6816 	243, /* Sep */
6817 	273, /* Oct */
6818 	304, /* Nov */
6819 	334, /* Dec */
6820 	365
6821 ];
6822 
6823 /**
6824  * the cumulative lengths of each month, for a leap year 
6825  * @private
6826  * @const
6827  * @type Array.<number>
6828  */
6829 GregRataDie.cumMonthLengthsLeap = [
6830 	0,   /* Jan */
6831 	31,  /* Feb */
6832 	60,  /* Mar */
6833 	91,  /* Apr */
6834 	121, /* May */
6835 	152, /* Jun */
6836 	182, /* Jul */
6837 	213, /* Aug */
6838 	244, /* Sep */
6839 	274, /* Oct */
6840 	305, /* Nov */
6841 	335, /* Dec */
6842 	366
6843 ];
6844 
6845 /**
6846  * Calculate the Rata Die (fixed day) number of the given date.
6847  * 
6848  * @private
6849  * @param {Object} date the date components to calculate the RD from
6850  */
6851 GregRataDie.prototype._setDateComponents = function(date) {
6852 	var year = parseInt(date.year, 10) || 0;
6853 	var month = parseInt(date.month, 10) || 1;
6854 	var day = parseInt(date.day, 10) || 1;
6855 	var hour = parseInt(date.hour, 10) || 0;
6856 	var minute = parseInt(date.minute, 10) || 0;
6857 	var second = parseInt(date.second, 10) || 0;
6858 	var millisecond = parseInt(date.millisecond, 10) || 0;
6859 
6860 	var years = 365 * (year - 1) +
6861 		Math.floor((year-1)/4) -
6862 		Math.floor((year-1)/100) +
6863 		Math.floor((year-1)/400);
6864 	
6865 	var dayInYear = (month > 1 ? GregRataDie.cumMonthLengths[month-1] : 0) +
6866 		day +
6867 		(GregorianCal.prototype.isLeapYear.call(this.cal, year) && month > 2 ? 1 : 0);
6868 	var rdtime = (hour * 3600000 +
6869 		minute * 60000 +
6870 		second * 1000 +
6871 		millisecond) / 
6872 		86400000; 
6873 	/*
6874 	debug("getRataDie: converting " +  JSON.stringify(this));
6875 	debug("getRataDie: year is " +  years);
6876 	debug("getRataDie: day in year is " +  dayInYear);
6877 	debug("getRataDie: rdtime is " +  rdtime);
6878 	debug("getRataDie: rd is " +  (years + dayInYear + rdtime));
6879 	*/
6880 	
6881 	/**
6882 	 * @type {number|undefined} the RD number of this Gregorian date
6883 	 */
6884 	this.rd = years + dayInYear + rdtime;
6885 };
6886 
6887 /**
6888  * Return the rd number of the particular day of the week on or before the 
6889  * given rd. eg. The Sunday on or before the given rd.
6890  * @private
6891  * @param {number} rd the rata die date of the reference date
6892  * @param {number} dayOfWeek the day of the week that is being sought relative 
6893  * to the current date
6894  * @return {number} the rd of the day of the week
6895  */
6896 GregRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
6897 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek, 7);
6898 };
6899 
6900 
6901 /*< TimeZone.js */
6902 /*
6903  * TimeZone.js - Definition of a time zone class
6904  * 
6905  * Copyright © 2012-2015, JEDLSoft
6906  *
6907  * Licensed under the Apache License, Version 2.0 (the "License");
6908  * you may not use this file except in compliance with the License.
6909  * You may obtain a copy of the License at
6910  *
6911  *     http://www.apache.org/licenses/LICENSE-2.0
6912  *
6913  * Unless required by applicable law or agreed to in writing, software
6914  * distributed under the License is distributed on an "AS IS" BASIS,
6915  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6916  *
6917  * See the License for the specific language governing permissions and
6918  * limitations under the License.
6919  */
6920 
6921 /*
6922 !depends 
6923 ilib.js 
6924 Locale.js
6925 LocaleInfo.js
6926 Utils.js
6927 MathUtils.js
6928 JSUtils.js
6929 GregRataDie.js
6930 IString.js
6931 CalendarFactory.js
6932 */
6933 
6934 // !data localeinfo zoneinfo
6935 
6936 
6937 
6938 
6939 /**
6940  * @class
6941  * Create a time zone instance. 
6942  * 
6943  * This class reports and transforms
6944  * information about particular time zones.<p>
6945  * 
6946  * The options parameter may contain any of the following properties:
6947  * 
6948  * <ul>
6949  * <li><i>id</i> - The id of the requested time zone such as "Europe/London" or 
6950  * "America/Los_Angeles". These are taken from the IANA time zone database. (See
6951  * http://www.iana.org/time-zones for more information.) <p>
6952  * 
6953  * There is one special 
6954  * time zone that is not taken from the IANA database called simply "local". In
6955  * this case, this class will attempt to discover the current time zone and
6956  * daylight savings time settings by calling standard Javascript classes to 
6957  * determine the offsets from UTC. 
6958  * 
6959  * <li><i>locale</i> - The locale for this time zone.
6960  * 
6961  * <li><i>offset</i> - Choose the time zone based on the offset from UTC given in
6962  * number of minutes (negative is west, positive is east).
6963  * 
6964  * <li><i>onLoad</i> - a callback function to call when the data is fully 
6965  * loaded. When the onLoad option is given, this class will attempt to
6966  * load any missing locale data using the ilib loader callback.
6967  * When the data is loaded, the onLoad function is called with the current 
6968  * instance as a parameter. 
6969  * 
6970  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
6971  * asynchronously. If this option is given as "false", then the "onLoad"
6972  * callback must be given, as the instance returned from this constructor will
6973  * not be usable for a while.
6974  *  
6975  * <li><i>loadParams</i> - an object containing parameters to pass to the 
6976  * loader callback function when locale data is missing. The parameters are not
6977  * interpretted or modified in any way. They are simply passed along. The object 
6978  * may contain any property/value pairs as long as the calling code is in
6979  * agreement with the loader callback function as to what those parameters mean.
6980  * </ul>
6981  * 
6982  * There is currently no way in the ECMAscript
6983  * standard to tell which exact time zone is currently in use. Choosing the
6984  * id "locale" or specifying an explicit offset will not give a specific time zone, 
6985  * as it is impossible to tell with certainty which zone the offsets 
6986  * match.<p>
6987  * 
6988  * When the id "local" is given or the offset option is specified, this class will
6989  * have the following behaviours:
6990  * <ul>
6991  * <li>The display name will always be given as the RFC822 style, no matter what
6992  * style is requested
6993  * <li>The id will also be returned as the RFC822 style display name
6994  * <li>When the offset is explicitly given, this class will assume the time zone 
6995  * does not support daylight savings time, and the offsets will be calculated 
6996  * the same way year round.
6997  * <li>When the offset is explicitly given, the inDaylightSavings() method will 
6998  * always return false.
6999  * <li>When the id "local" is given, this class will attempt to determine the 
7000  * daylight savings time settings by examining the offset from UTC on Jan 1
7001  * and June 1 of the current year. If they are different, this class assumes
7002  * that the local time zone uses DST. When the offset for a particular date is
7003  * requested, it will use the built-in Javascript support to determine the 
7004  * offset for that date.
7005  * </ul> 
7006  * 
7007  * If a more specific time zone is 
7008  * needed with display names and known start/stop times for DST, use the "id" 
7009  * property instead to specify the time zone exactly. You can perhaps ask the
7010  * user which time zone they prefer so that your app does not need to guess.<p>
7011  * 
7012  * If the id and the offset are both not given, the default time zone for the 
7013  * locale is retrieved from
7014  * the locale info. If the locale is not specified, the default locale for the
7015  * library is used.<p>
7016  * 
7017  * Because this class was designed for use in web sites, and the vast majority
7018  * of dates and times being formatted are recent date/times, this class is simplified
7019  * by not implementing historical time zones. That is, when governments change the 
7020  * time zone rules for a particular zone, only the latest such rule is implemented 
7021  * in this class. That means that determining the offset for a date that is prior 
7022  * to the last change may give the wrong result. Historical time zone calculations
7023  * may be implemented in a later version of iLib if there is enough demand for it,
7024  * but it would entail a much larger set of time zone data that would have to be
7025  * loaded.  
7026  * 
7027  * 
7028  * @constructor
7029  * @param {Object} options Options guiding the construction of this time zone instance
7030  */
7031 var TimeZone = function(options) {
7032 	this.sync = true;
7033 	this.locale = new Locale();
7034 	this.isLocal = false;
7035 	
7036 	if (options) {
7037 		if (options.locale) {
7038 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
7039 		}
7040 		
7041 		if (options.id) {
7042 			var id = options.id.toString();
7043 			if (id === 'local') {
7044 				this.isLocal = true;
7045 				
7046 				// use standard Javascript Date to figure out the time zone offsets
7047 				var now = new Date(), 
7048 					jan1 = new Date(now.getFullYear(), 0, 1),  // months in std JS Date object are 0-based
7049 					jun1 = new Date(now.getFullYear(), 5, 1);
7050 				
7051 				// Javascript's method returns the offset backwards, so we have to
7052 				// take the negative to get the correct offset
7053 				this.offsetJan1 = -jan1.getTimezoneOffset();
7054 				this.offsetJun1 = -jun1.getTimezoneOffset();
7055 				// the offset of the standard time for the time zone is always the one that is closest 
7056 				// to negative infinity of the two, no matter whether you are in the northern or southern 
7057 				// hemisphere, east or west
7058 				this.offset = Math.min(this.offsetJan1, this.offsetJun1);
7059 			}
7060 			this.id = id;
7061 		} else if (options.offset) {
7062 			this.offset = (typeof(options.offset) === 'string') ? parseInt(options.offset, 10) : options.offset;
7063 			this.id = this.getDisplayName(undefined, undefined);
7064 		}
7065 		
7066 		if (typeof(options.sync) !== 'undefined') {
7067 			this.sync = !!options.sync;
7068 		}
7069 		
7070 		this.loadParams = options.loadParams;
7071 		this.onLoad = options.onLoad;
7072 	}
7073 
7074 	//console.log("timezone: locale is " + this.locale);
7075 	
7076 	if (!this.id) {
7077 		new LocaleInfo(this.locale, {
7078 			sync: this.sync,
7079 			loadParams: this.loadParams,
7080 			onLoad: ilib.bind(this, function (li) {
7081 				this.id = li.getTimeZone() || "Etc/UTC";
7082 				this._loadtzdata();
7083 			})
7084 		});
7085 	} else {
7086 		this._loadtzdata();
7087 	}
7088 
7089 	//console.log("localeinfo is: " + JSON.stringify(this.locinfo));
7090 	//console.log("id is: " + JSON.stringify(this.id));
7091 };
7092 
7093 /*
7094  * Explanation of the compressed time zone info properties.
7095  * {
7096  *     "o": "8:0",      // offset from UTC
7097  *     "f": "W{c}T",    // standard abbreviation. For time zones that observe DST, the {c} replacement is replaced with the 
7098  *                      // letter in the e.c or s.c properties below 
7099  *     "e": {           // info about the end of DST
7100  *         "j": 78322.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 
7101  *                      // "t" properties, but not both sets.
7102  *         "m": 3,      // month that it ends
7103  *         "r": "l0",   // rule for the day it ends "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7". 
7104  *                      // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >=
7105  *         "t": "2:0",  // time of day that the DST turns off, hours:minutes
7106  *         "c": "S"     // character to replace into the abbreviation for standard time 
7107  *     },
7108  *     "s": {           // info about the start of DST
7109  *         "j": 78189.5 // Julian day when the transition happens. Either specify the "j" property or all of the "m", "r", and 
7110  *                      // "t" properties, but not both sets.
7111  *         "m": 10,     // month that it starts
7112  *         "r": "l0",   // rule for the day it starts "l" = "last", numbers are Sun=0 through Sat=6. Other syntax is "0>7".
7113  *                      // This means the 0-day (Sun) after the 7th of the month. Other possible operators are <, >, <=, >=
7114  *         "t": "2:0",  // time of day that the DST turns on, hours:minutes
7115  *         "v": "1:0",  // amount of time saved in hours:minutes
7116  *         "c": "D"     // character to replace into the abbreviation for daylight time
7117  *     },
7118  *     "c": "AU",       // ISO code for the country that contains this time zone
7119  *     "n": "W. Australia {c} Time"
7120  *                      // long English name of the zone. The {c} replacement is for the word "Standard" or "Daylight" as appropriate
7121  * }
7122  */
7123 TimeZone.prototype._loadtzdata = function () {
7124 	var zoneName = this.id.replace(/-/g, "m").replace(/\+/g, "p");
7125 	// console.log("id is: " + JSON.stringify(this.id));
7126 	// console.log("zoneinfo is: " + JSON.stringify(ilib.data.zoneinfo[zoneName]));
7127 	if (!ilib.data.zoneinfo[zoneName] && typeof(this.offset) === 'undefined') {
7128 		Utils.loadData({
7129 			object: "TimeZone", 
7130 			nonlocale: true,	// locale independent 
7131 			name: "zoneinfo/" + this.id + ".json", 
7132 			sync: this.sync, 
7133 			loadParams: this.loadParams, 
7134 			callback: ilib.bind(this, function (tzdata) {
7135 				if (tzdata && !JSUtils.isEmpty(tzdata)) {
7136 					ilib.data.zoneinfo[zoneName] = tzdata;
7137 				}
7138 				this._initZone(zoneName);
7139 			})
7140 		});
7141 	} else {
7142 		this._initZone(zoneName);
7143 	}
7144 };
7145 
7146 TimeZone.prototype._initZone = function(zoneName) {
7147 	/** 
7148 	 * @private
7149 	 * @type {{o:string,f:string,e:Object.<{m:number,r:string,t:string,z:string}>,s:Object.<{m:number,r:string,t:string,z:string,v:string,c:string}>,c:string,n:string}} 
7150 	 */
7151 	this.zone = ilib.data.zoneinfo[zoneName];
7152 	if (!this.zone && typeof(this.offset) === 'undefined') {
7153 		this.id = "Etc/UTC";
7154 		this.zone = ilib.data.zoneinfo[this.id];
7155 	}
7156 	
7157 	this._calcDSTSavings();
7158 	
7159 	if (typeof(this.offset) === 'undefined' && this.zone.o) {
7160 		var offsetParts = this._offsetStringToObj(this.zone.o);
7161 		/**
7162 		 * @private
7163 		 * @type {number} raw offset from UTC without DST, in minutes
7164 		 */
7165 		this.offset = (Math.abs(offsetParts.h || 0) * 60 + (offsetParts.m || 0)) * MathUtils.signum(offsetParts.h || 0);
7166 	}
7167 	
7168 	if (this.onLoad && typeof(this.onLoad) === 'function') {
7169 		this.onLoad(this);
7170 	}
7171 };
7172 
7173 /** @private */
7174 TimeZone._marshallIds = function (country, sync, callback) {
7175 	var tz, ids = [];
7176 	
7177 	if (!country) {
7178 		// local is a special zone meaning "the local time zone according to the JS engine we are running upon"
7179 		ids.push("local");
7180 		for (tz in ilib.data.timezones) {
7181 			if (ilib.data.timezones[tz]) {
7182 				ids.push(ilib.data.timezones[tz]);
7183 			}
7184 		}
7185 		if (typeof(callback) === 'function') {
7186 			callback(ids);
7187 		}
7188 	} else {
7189 		if (!ilib.data.zoneinfo.zonetab) {
7190 			Utils.loadData({
7191 				object: "TimeZone", 
7192 				nonlocale: true,	// locale independent 
7193 				name: "zoneinfo/zonetab.json", 
7194 				sync: sync, 
7195 				callback: ilib.bind(this, function (tzdata) {
7196 					if (tzdata) {
7197 						ilib.data.zoneinfo.zonetab = tzdata;
7198 					}
7199 					
7200 					ids = ilib.data.zoneinfo.zonetab[country];
7201 					
7202 					if (typeof(callback) === 'function') {
7203 						callback(ids);
7204 					}
7205 				})
7206 			});
7207 		} else {
7208 			ids = ilib.data.zoneinfo.zonetab[country];
7209 			if (typeof(callback) === 'function') {
7210 				callback(ids);
7211 			}
7212 		}
7213 	}
7214 	
7215 	return ids;
7216 };
7217 
7218 /**
7219  * Return an array of available zone ids that the constructor knows about.
7220  * The country parameter is optional. If it is not given, all time zones will
7221  * be returned. If it specifies a country code, then only time zones for that
7222  * country will be returned.
7223  * 
7224  * @param {string|undefined} country country code for which time zones are being sought
7225  * @param {boolean} sync whether to find the available ids synchronously (true) or asynchronously (false)
7226  * @param {function(Array.<string>)} onLoad callback function to call when the data is finished loading
7227  * @return {Array.<string>} an array of zone id strings
7228  */
7229 TimeZone.getAvailableIds = function (country, sync, onLoad) {
7230 	var tz, ids = [];
7231 	
7232 	if (typeof(sync) !== 'boolean') {
7233 		sync = true;
7234 	}
7235 	
7236 	if (ilib.data.timezones.length === 0) {
7237 		if (typeof(ilib._load) !== 'undefined' && typeof(ilib._load.listAvailableFiles) === 'function') {
7238 			ilib._load.listAvailableFiles(sync, function(hash) {
7239 				for (var dir in hash) {
7240 					var files = hash[dir];
7241 					if (ilib.isArray(files)) {
7242 						files.forEach(function (filename) {
7243 							if (filename && filename.match(/^zoneinfo/)) {
7244 								ilib.data.timezones.push(filename.replace(/^zoneinfo\//, "").replace(/\.json$/, ""));
7245 							}
7246 						});
7247 					}
7248 				}
7249 				ids = TimeZone._marshallIds(country, sync, onLoad);
7250 			});
7251 		} else {
7252 			for (tz in ilib.data.zoneinfo) {
7253 				if (ilib.data.zoneinfo[tz]) {
7254 					ilib.data.timezones.push(tz);
7255 				}
7256 			}
7257 			ids = TimeZone._marshallIds(country, sync, onLoad);
7258 		}
7259 	} else {
7260 		ids = TimeZone._marshallIds(country, sync, onLoad);
7261 	}
7262 	
7263 	return ids;
7264 };
7265 
7266 /**
7267  * Return the id used to uniquely identify this time zone.
7268  * @return {string} a unique id for this time zone
7269  */
7270 TimeZone.prototype.getId = function () {
7271 	return this.id.toString();
7272 };
7273 
7274 /**
7275  * Return the abbreviation that is used for the current time zone on the given date.
7276  * The date may be in DST or during standard time, and many zone names have different
7277  * abbreviations depending on whether or not the date is falls within DST.<p>
7278  * 
7279  * There are two styles that are supported:
7280  * 
7281  * <ol>
7282  * <li>standard - returns the 3 to 5 letter abbreviation of the time zone name such 
7283  * as "CET" for "Central European Time" or "PDT" for "Pacific Daylight Time"
7284  * <li>rfc822 - returns an RFC 822 style time zone specifier, which specifies more
7285  * explicitly what the offset is from UTC
7286  * <li>long - returns the long name of the zone in English
7287  * </ol>
7288  *  
7289  * @param {IDate=} date a date to determine if it is in daylight time or standard time
7290  * @param {string=} style one of "standard" or "rfc822". Default if not specified is "standard"
7291  * @return {string} the name of the time zone, abbreviated according to the style 
7292  */
7293 TimeZone.prototype.getDisplayName = function (date, style) {
7294     var temp;
7295 	style = (this.isLocal || typeof(this.zone) === 'undefined') ? "rfc822" : (style || "standard");
7296 	switch (style) {
7297 		default:
7298 		case 'standard':
7299 			if (this.zone.f && this.zone.f !== "zzz") {
7300 				if (this.zone.f.indexOf("{c}") !== -1) {
7301 					var letter = "";
7302 					letter = this.inDaylightTime(date) ? this.zone.s && this.zone.s.c : this.zone.e && this.zone.e.c; 
7303 					temp = new IString(this.zone.f);
7304 					return temp.format({c: letter || ""});
7305 				}
7306 				return this.zone.f;
7307 			} 
7308 			temp = "GMT" + this.zone.o;
7309 			if (this.inDaylightTime(date)) {
7310 				temp += "+" + this.zone.s.v;
7311 			}
7312 			return temp;
7313 
7314 		case 'rfc822':
7315 			var offset = this.getOffset(date), // includes the DST if applicable
7316 				ret = "UTC",
7317 				hour = offset.h || 0,
7318 				minute = offset.m || 0;
7319 			
7320 			if (hour !== 0) {
7321 				ret += (hour > 0) ? "+" : "-";
7322 				if (Math.abs(hour) < 10) {
7323 					ret += "0";
7324 				}
7325 				ret += (hour < 0) ? -hour : hour;
7326 				if (minute < 10) {
7327 					ret += "0";
7328 				}
7329 				ret += minute;
7330 			}
7331 			return ret;
7332 
7333 		case 'long':
7334 			if (this.zone.n) {
7335 				if (this.zone.n.indexOf("{c}") !== -1) {
7336 					var str = this.inDaylightTime(date) ? "Daylight" : "Standard"; 
7337 					temp = new IString(this.zone.n);
7338 					return temp.format({c: str || ""});
7339 				}
7340 				return this.zone.n;
7341 			}
7342 			temp = "GMT" + this.zone.o;
7343 			if (this.inDaylightTime(date)) {
7344 				temp += "+" + this.zone.s.v;
7345 			}
7346 			return temp;
7347 	}
7348 };
7349 
7350 /**
7351  * Convert the offset string to an object with an h, m, and possibly s property
7352  * to indicate the hours, minutes, and seconds.
7353  * 
7354  * @private
7355  * @param {string} str the offset string to convert to an object
7356  * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset for the zone at 
7357  * the given date/time, in hours, minutes, and seconds
7358  */
7359 TimeZone.prototype._offsetStringToObj = function (str) {
7360 	var offsetParts = (typeof(str) === 'string') ? str.split(":") : [],
7361 		ret = {h:0},
7362 		temp;
7363 	
7364 	if (offsetParts.length > 0) {
7365 		ret.h = parseInt(offsetParts[0], 10);
7366 		if (offsetParts.length > 1) {
7367 			temp = parseInt(offsetParts[1], 10);
7368 			if (temp) {
7369 				ret.m = temp;
7370 			}
7371 			if (offsetParts.length > 2) {
7372 				temp = parseInt(offsetParts[2], 10);
7373 				if (temp) {
7374 					ret.s = temp;
7375 				}
7376 			}
7377 		}
7378 	}
7379 
7380 	return ret;
7381 };
7382 
7383 /**
7384  * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 
7385  * time is in effect at the given date/time, this method will return the offset value 
7386  * adjusted by the amount of daylight saving.
7387  * @param {IDate=} date the date for which the offset is needed
7388  * @return {Object.<{h:number,m:number}>} an object giving the offset for the zone at 
7389  * the given date/time, in hours, minutes, and seconds  
7390  */
7391 TimeZone.prototype.getOffset = function (date) {
7392 	if (!date) {
7393 		return this.getRawOffset();
7394 	}
7395 	var offset = this.getOffsetMillis(date)/60000;
7396 	
7397 	var hours = MathUtils.down(offset/60),
7398 		minutes = Math.abs(offset) - Math.abs(hours)*60;
7399 
7400 	var ret = {
7401 		h: hours
7402 	};
7403 	if (minutes != 0) {
7404 		ret.m = minutes;
7405 	}
7406 	return ret;
7407 };
7408 
7409 /**
7410  * Returns the offset of this time zone from UTC at the given date/time expressed in 
7411  * milliseconds. If daylight saving 
7412  * time is in effect at the given date/time, this method will return the offset value 
7413  * adjusted by the amount of daylight saving. Negative numbers indicate offsets west
7414  * of UTC and conversely, positive numbers indicate offset east of UTC.
7415  *  
7416  * @param {IDate=} date the date for which the offset is needed, or null for the
7417  * present date
7418  * @return {number} the number of milliseconds of offset from UTC that the given date is
7419  */
7420 TimeZone.prototype.getOffsetMillis = function (date) {
7421 	var ret;
7422 	
7423 	// check if the dst property is defined -- the intrinsic JS Date object doesn't work so
7424 	// well if we are in the overlap time at the end of DST
7425 	if (this.isLocal && typeof(date.dst) === 'undefined') {
7426 		var d = (!date) ? new Date() : new Date(date.getTimeExtended());
7427 		return -d.getTimezoneOffset() * 60000;
7428 	} 
7429 	
7430 	ret = this.offset;
7431 	
7432 	if (date && this.inDaylightTime(date)) {
7433 		ret += this.dstSavings;
7434 	}
7435 	
7436 	return ret * 60000;
7437 };
7438 
7439 /**
7440  * Return the offset in milliseconds when the date has an RD number in wall
7441  * time rather than in UTC time.
7442  * @protected
7443  * @param date the date to check in wall time
7444  * @returns {number} the number of milliseconds of offset from UTC that the given date is
7445  */
7446 TimeZone.prototype._getOffsetMillisWallTime = function (date) {
7447 	var ret;
7448 	
7449 	ret = this.offset;
7450 	
7451 	if (date && this.inDaylightTime(date, true)) {
7452 		ret += this.dstSavings;
7453 	}
7454 	
7455 	return ret * 60000;
7456 };
7457 
7458 /**
7459  * Returns the offset of this time zone from UTC at the given date/time. If daylight saving 
7460  * time is in effect at the given date/time, this method will return the offset value 
7461  * adjusted by the amount of daylight saving.
7462  * @param {IDate=} date the date for which the offset is needed
7463  * @return {string} the offset for the zone at the given date/time as a string in the 
7464  * format "h:m:s" 
7465  */
7466 TimeZone.prototype.getOffsetStr = function (date) {
7467 	var offset = this.getOffset(date),
7468 		ret;
7469 	
7470 	ret = offset.h;
7471 	if (typeof(offset.m) !== 'undefined') {
7472 		ret += ":" + offset.m;
7473 		if (typeof(offset.s) !== 'undefined') {
7474 			ret += ":" + offset.s;
7475 		}
7476 	} else {
7477 		ret += ":0";
7478 	}
7479 	
7480 	return ret;
7481 };
7482 
7483 /**
7484  * Gets the offset from UTC for this time zone.
7485  * @return {Object.<{h:number,m:number,s:number}>} an object giving the offset from 
7486  * UTC for this time zone, in hours, minutes, and seconds 
7487  */
7488 TimeZone.prototype.getRawOffset = function () {
7489 	var hours = MathUtils.down(this.offset/60),
7490 		minutes = Math.abs(this.offset) - Math.abs(hours)*60;
7491 	
7492 	var ret = {
7493 		h: hours
7494 	};
7495 	if (minutes != 0) {
7496 		ret.m = minutes;
7497 	}
7498 	return ret;
7499 };
7500 
7501 /**
7502  * Gets the offset from UTC for this time zone expressed in milliseconds. Negative numbers
7503  * indicate zones west of UTC, and positive numbers indicate zones east of UTC.
7504  * 
7505  * @return {number} an number giving the offset from 
7506  * UTC for this time zone in milliseconds 
7507  */
7508 TimeZone.prototype.getRawOffsetMillis = function () {
7509 	return this.offset * 60000;
7510 };
7511 
7512 /**
7513  * Gets the offset from UTC for this time zone without DST savings.
7514  * @return {string} the offset from UTC for this time zone, in the format "h:m:s" 
7515  */
7516 TimeZone.prototype.getRawOffsetStr = function () {
7517 	var off = this.getRawOffset();
7518 	return off.h + ":" + (off.m || "0");
7519 };
7520 
7521 /**
7522  * Return the amount of time in hours:minutes that the clock is advanced during
7523  * daylight savings time.
7524  * @return {Object.<{h:number,m:number,s:number}>} the amount of time that the 
7525  * clock advances for DST in hours, minutes, and seconds 
7526  */
7527 TimeZone.prototype.getDSTSavings = function () {
7528 	if (this.isLocal) {
7529 		// take the absolute because the difference in the offsets may be positive or
7530 		// negative, depending on the hemisphere
7531 		var savings = Math.abs(this.offsetJan1 - this.offsetJun1);
7532 		var hours = MathUtils.down(savings/60),
7533 			minutes = savings - hours*60;
7534 		return {
7535 			h: hours,
7536 			m: minutes
7537 		};
7538 	} else if (this.zone && this.zone.s) {
7539 		return this._offsetStringToObj(this.zone.s.v);	// this.zone.start.savings
7540 	}
7541 	return {h:0};
7542 };
7543 
7544 /**
7545  * Return the amount of time in hours:minutes that the clock is advanced during
7546  * daylight savings time.
7547  * @return {string} the amount of time that the clock advances for DST in the
7548  * format "h:m:s"
7549  */
7550 TimeZone.prototype.getDSTSavingsStr = function () {
7551 	if (this.isLocal) {
7552 		var savings = this.getDSTSavings();
7553 		return savings.h + ":" + savings.m;
7554 	} else if (typeof(this.offset) !== 'undefined' && this.zone && this.zone.s) {
7555 		return this.zone.s.v;	// this.zone.start.savings
7556 	}
7557 	return "0:0";
7558 };
7559 
7560 /**
7561  * return the rd of the start of DST transition for the given year
7562  * @protected
7563  * @param {Object} rule set of rules
7564  * @param {number} year year to check
7565  * @return {number} the rd of the start of DST for the year
7566  */
7567 TimeZone.prototype._calcRuleStart = function (rule, year) {
7568 	var type = "=", 
7569 		weekday = 0, 
7570 		day, 
7571 		refDay, 
7572 		cal, 
7573 		hour = 0, 
7574 		minute = 0, 
7575 		second = 0,
7576 		time,
7577 		i;
7578 	
7579 	if (typeof(rule.j) !== 'undefined') {
7580 		refDay = new GregRataDie({
7581 			julianday: rule.j
7582 		});
7583 	} else {
7584 		if (rule.r.charAt(0) == 'l' || rule.r.charAt(0) == 'f') {
7585 			cal = CalendarFactory({type: "gregorian"}); // can be synchronous
7586 			type = rule.r.charAt(0);
7587 			weekday = parseInt(rule.r.substring(1), 10);
7588 			day = (type === 'l') ? cal.getMonLength(rule.m, year) : 1;
7589 			//console.log("_calcRuleStart: Calculating the " + 
7590 			//		(rule.r.charAt(0) == 'f' ? "first " : "last ") + weekday + 
7591 			//		" of month " + rule.m);
7592 		} else {
7593 			i = rule.r.indexOf('<');
7594 			if (i == -1) {
7595 				i = rule.r.indexOf('>');
7596 			}
7597 			
7598 			if (i != -1) {
7599 				type = rule.r.charAt(i);
7600 				weekday = parseInt(rule.r.substring(0, i), 10);
7601 				day = parseInt(rule.r.substring(i+1), 10); 
7602 				//console.log("_calcRuleStart: Calculating the " + weekday + 
7603 				//		type + day + " of month " + rule.m);
7604 			} else {
7605 				day = parseInt(rule.r, 10);
7606 				//console.log("_calcRuleStart: Calculating the " + day + " of month " + rule.m);
7607 			}
7608 		}
7609 	
7610 		if (rule.t) {
7611 			time = rule.t.split(":");
7612 			hour = parseInt(time[0], 10);
7613 			if (time.length > 1) {
7614 				minute = parseInt(time[1], 10);
7615 				if (time.length > 2) {
7616 					second = parseInt(time[2], 10);
7617 				}
7618 			}
7619 		}
7620 		//console.log("calculating rd of " + year + "/" + rule.m + "/" + day);
7621 		refDay = new GregRataDie({
7622 			year: year, 
7623 			month: rule.m, 
7624 			day: day, 
7625 			hour: hour, 
7626 			minute: minute, 
7627 			second: second
7628 		});
7629 	}
7630 	//console.log("refDay is " + JSON.stringify(refDay));
7631 	var d = refDay.getRataDie();
7632 	
7633 	switch (type) {
7634 		case 'l':
7635 		case '<':
7636 			//console.log("returning " + refDay.onOrBefore(rd, weekday));
7637 			d = refDay.onOrBefore(weekday); 
7638 			break;
7639 		case 'f':
7640 		case '>':
7641 			//console.log("returning " + refDay.onOrAfterRd(rd, weekday));
7642 			d = refDay.onOrAfter(weekday); 
7643 			break;
7644 	}
7645 	return d;
7646 };
7647 
7648 /**
7649  * @private
7650  */
7651 TimeZone.prototype._calcDSTSavings = function () {
7652 	var saveParts = this.getDSTSavings();
7653 	
7654 	/**
7655 	 * @private
7656 	 * @type {number} savings in minutes when DST is in effect 
7657 	 */
7658 	this.dstSavings = (Math.abs(saveParts.h || 0) * 60 + (saveParts.m || 0)) * MathUtils.signum(saveParts.h || 0);
7659 };
7660 
7661 /**
7662  * @private
7663  */
7664 TimeZone.prototype._getDSTStartRule = function (year) {
7665 	// TODO: update this when historic/future zones are supported
7666 	return this.zone.s;
7667 };
7668 
7669 /**
7670  * @private
7671  */
7672 TimeZone.prototype._getDSTEndRule = function (year) {
7673 	// TODO: update this when historic/future zones are supported
7674 	return this.zone.e;
7675 };
7676 
7677 /**
7678  * Returns whether or not the given date is in daylight saving time for the current
7679  * zone. Note that daylight savings time is observed for the summer. Because
7680  * the seasons are reversed, daylight savings time in the southern hemisphere usually
7681  * runs from the end of the year through New Years into the first few months of the
7682  * next year. This method will correctly calculate the start and end of DST for any
7683  * location.
7684  * 
7685  * @param {IDate=} date a date for which the info about daylight time is being sought,
7686  * or undefined to tell whether we are currently in daylight savings time
7687  * @param {boolean=} wallTime if true, then the given date is in wall time. If false or
7688  * undefined, it is in the usual UTC time.
7689  * @return {boolean} true if the given date is in DST for the current zone, and false
7690  * otherwise.
7691  */
7692 TimeZone.prototype.inDaylightTime = function (date, wallTime) {
7693 	var rd, startRd, endRd, year;
7694 
7695 	if (this.isLocal) {
7696 		// check if the dst property is defined -- the intrinsic JS Date object doesn't work so
7697 		// well if we are in the overlap time at the end of DST, so we have to work around that
7698 		// problem by adding in the savings ourselves
7699 		var offset = this.offset * 60000;
7700 		if (typeof(date.dst) !== 'undefined' && !date.dst) {
7701 			offset += this.dstSavings * 60000;
7702 		}
7703 
7704 		var d = new Date(date ? date.getTimeExtended() - offset: undefined);
7705 		// the DST offset is always the one that is closest to positive infinity, no matter 
7706 		// if you are in the northern or southern hemisphere, east or west
7707 		var dst = Math.max(this.offsetJan1, this.offsetJun1);
7708 		return (-d.getTimezoneOffset() === dst);
7709 	}
7710 	
7711 	if (!date || !date.cal || date.cal.type !== "gregorian") {
7712 		// convert to Gregorian so that we can tell if it is in DST or not
7713 		var time = date && typeof(date.getTimeExtended) === 'function' ? date.getTimeExtended() : undefined;
7714 		rd = new GregRataDie({unixtime: time}).getRataDie();
7715 		year = new Date(time).getUTCFullYear();
7716 	} else {
7717 		rd = date.rd.getRataDie();
7718 		year = date.year;
7719 	}
7720 	// rd should be a Gregorian RD number now, in UTC
7721 	
7722 	// if we aren't using daylight time in this zone for the given year, then we are 
7723 	// not in daylight time
7724 	if (!this.useDaylightTime(year)) {
7725 		return false;
7726 	}
7727 	
7728 	// these calculate the start/end in local wall time
7729 	var startrule = this._getDSTStartRule(year);
7730 	var endrule = this._getDSTEndRule(year);
7731 	startRd = this._calcRuleStart(startrule, year);
7732 	endRd = this._calcRuleStart(endrule, year);
7733 	
7734 	if (wallTime) {
7735 		// rd is in wall time, so we have to make sure to skip the missing time
7736 		// at the start of DST when standard time ends and daylight time begins
7737 		startRd += this.dstSavings/1440;
7738 	} else {
7739 		// rd is in UTC, so we have to convert the start/end to UTC time so 
7740 		// that they can be compared directly to the UTC rd number of the date
7741 		
7742 		// when DST starts, time is standard time already, so we only have
7743 		// to subtract the offset to get to UTC and not worry about the DST savings
7744 		startRd -= this.offset/1440;  
7745 		
7746 		// when DST ends, time is in daylight time already, so we have to
7747 		// subtract the DST savings to get back to standard time, then the
7748 		// offset to get to UTC
7749 		endRd -= (this.offset + this.dstSavings)/1440;
7750 	}
7751 	
7752 	// In the northern hemisphere, the start comes first some time in spring (Feb-Apr), 
7753 	// then the end some time in the fall (Sept-Nov). In the southern
7754 	// hemisphere, it is the other way around because the seasons are reversed. Standard
7755 	// time is still in the winter, but the winter months are May-Aug, and daylight 
7756 	// savings time usually starts Aug-Oct of one year and runs through Mar-May of the 
7757 	// next year.
7758 	if (rd < endRd && endRd - rd <= this.dstSavings/1440 && typeof(date.dst) === 'boolean') {
7759 		// take care of the magic overlap time at the end of DST
7760 		return date.dst;
7761 	}
7762 	if (startRd < endRd) {
7763 		// northern hemisphere
7764 		return (rd >= startRd && rd < endRd) ? true : false;
7765 	} 
7766 	// southern hemisphere
7767 	return (rd >= startRd || rd < endRd) ? true : false;
7768 };
7769 
7770 /**
7771  * Returns true if this time zone switches to daylight savings time at some point
7772  * in the year, and false otherwise.
7773  * @param {number} year Whether or not the time zone uses daylight time in the given year. If
7774  * this parameter is not given, the current year is assumed.
7775  * @return {boolean} true if the time zone uses daylight savings time
7776  */
7777 TimeZone.prototype.useDaylightTime = function (year) {
7778 	
7779 	// this zone uses daylight savings time iff there is a rule defining when to start
7780 	// and when to stop the DST
7781 	return (this.isLocal && this.offsetJan1 !== this.offsetJun1) ||
7782 		(typeof(this.zone) !== 'undefined' && 
7783 		typeof(this.zone.s) !== 'undefined' && 
7784 		typeof(this.zone.e) !== 'undefined');
7785 };
7786 
7787 /**
7788  * Returns the ISO 3166 code of the country for which this time zone is defined.
7789  * @return {string} the ISO 3166 code of the country for this zone
7790  */
7791 TimeZone.prototype.getCountry = function () {
7792 	return this.zone.c;
7793 };
7794 
7795 
7796 
7797 /*< ISet.js */
7798 /*
7799  * ISet.js - ilib Set class definition for platforms older than ES6
7800  * 
7801  * Copyright © 2015, JEDLSoft
7802  *
7803  * Licensed under the Apache License, Version 2.0 (the "License");
7804  * you may not use this file except in compliance with the License.
7805  * You may obtain a copy of the License at
7806  *
7807  *     http://www.apache.org/licenses/LICENSE-2.0
7808  *
7809  * Unless required by applicable law or agreed to in writing, software
7810  * distributed under the License is distributed on an "AS IS" BASIS,
7811  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7812  *
7813  * See the License for the specific language governing permissions and
7814  * limitations under the License.
7815  */
7816 
7817 /**
7818  * Create a new set with elements in the given array. The type of
7819  * the set is gleaned from the type of the first element in the
7820  * elements array, or the first element added to the set. The type
7821  * may be "string" or "number", and all elements will be returned
7822  * as elements of that type.
7823  * 
7824  * @class
7825  * @param {Array.<string|number>=} elements initial elements to add to the set
7826  * @constructor
7827  */
7828 var ISet = function(elements) {
7829 	this.elements = {};
7830 
7831 	if (elements && elements.length) {
7832 		for (var i = 0; i < elements.length; i++) {
7833 			this.elements[elements[i]] = true;
7834 		}
7835 		
7836 		this.type = typeof(elements[0]);
7837 	}
7838 };
7839 
7840 /**
7841  * @private
7842  */
7843 ISet.prototype._addOne = function(element) {
7844 	if (this.isEmpty()) {
7845 		this.type = typeof(element);
7846 	}
7847 	
7848 	if (!this.elements[element]) {
7849 		this.elements[element] = true;
7850 		return true;
7851 	}
7852 
7853 	return false;
7854 };
7855 
7856 /**
7857  * Adds the specified element or array of elements to this set if it is or they are not 
7858  * already present.
7859  * 
7860  * @param {*|Array.<*>} element element or array of elements to add
7861  * @return {boolean} true if this set did not already contain the specified element[s]
7862  */
7863 ISet.prototype.add = function(element) {
7864 	var ret = false;
7865 	
7866 	if (typeof(element) === "object") {
7867 		for (var i = 0; i < element.length; i++) {
7868 			ret = this._addOne(element[i]) || ret;
7869 		}
7870 	} else {
7871 		ret = this._addOne(element);
7872 	}
7873 	
7874 	return ret;
7875 };
7876 
7877 /**
7878  * Removes all of the elements from this set.
7879  */
7880 ISet.prototype.clear = function() {
7881 	this.elements = {};
7882 };
7883 
7884 /**
7885  * Returns true if this set contains the specified element.
7886  * @param {*} element the element to test
7887  * @return {boolean}
7888  */
7889 ISet.prototype.contains = function(element) {
7890 	return this.elements[element] || false;
7891 };
7892 
7893 /**
7894  * Returns true if this set contains no elements.
7895  * @return {boolean}
7896  */
7897 ISet.prototype.isEmpty = function() {
7898 	return (Object.keys(this.elements).length === 0);
7899 };
7900 
7901 /**
7902  * Removes the specified element from this set if it is present.
7903  * @param {*} element the element to remove
7904  * @return {boolean} true if the set contained the specified element
7905  */
7906 ISet.prototype.remove = function(element) {
7907 	if (this.elements[element]) {
7908 		delete this.elements[element];
7909 		return true;
7910 	}
7911 	
7912 	return false;
7913 };
7914 
7915 /**
7916  * Return the set as a javascript array.
7917  * @return {Array.<*>} the set represented as a javascript array
7918  */
7919 ISet.prototype.asArray = function() {
7920 	var keys = Object.keys(this.elements);
7921 	
7922 	// keys is an array of strings. Convert to numbers if necessary
7923 	if (this.type === "number") {
7924 		var tmp = [];
7925 		for (var i = 0; i < keys.length; i++) {
7926 			tmp.push(Number(keys[i]).valueOf());
7927 		}
7928 		keys = tmp;
7929 	}
7930 	
7931 	return keys;
7932 };
7933 
7934 /**
7935  * Represents the current set as json.
7936  * @return {string} the current set represented as json
7937  */
7938 ISet.prototype.toJson = function() {
7939 	return JSON.stringify(this.asArray());
7940 };
7941 
7942 /**
7943  * Convert to a javascript representation of this object.
7944  * In this case, it is a normal JS array.
7945  * @return {*} the JS representation of this object
7946  */
7947 ISet.prototype.toJS = function() {
7948 	return this.asArray();
7949 };
7950 
7951 /**
7952  * Convert from a js representation to an internal one.
7953  * @return {ISet|undefined} the current object, or undefined if the conversion did not work
7954  */
7955 ISet.prototype.fromJS = function(obj) {
7956 	return this.add(obj) ? this : undefined;
7957 };
7958 
7959 
7960 
7961 /*< DateFmt.js */
7962 /*
7963  * DateFmt.js - Date formatter definition
7964  * 
7965  * Copyright © 2012-2015, 2018, JEDLSoft
7966  *
7967  * Licensed under the Apache License, Version 2.0 (the "License");
7968  * you may not use this file except in compliance with the License.
7969  * You may obtain a copy of the License at
7970  *
7971  *     http://www.apache.org/licenses/LICENSE-2.0
7972  *
7973  * Unless required by applicable law or agreed to in writing, software
7974  * distributed under the License is distributed on an "AS IS" BASIS,
7975  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7976  *
7977  * See the License for the specific language governing permissions and
7978  * limitations under the License.
7979  */
7980 
7981 /*
7982 !depends 
7983 ilib.js 
7984 Locale.js 
7985 IDate.js
7986 DateFactory.js  
7987 ResBundle.js 
7988 CalendarFactory.js
7989 LocaleInfo.js
7990 TimeZone.js
7991 GregorianCal.js
7992 JSUtils.js
7993 Utils.js
7994 ISet.js
7995 */
7996 
7997 // !data dateformats sysres
7998 
7999 
8000 
8001 
8002 
8003 
8004 /**
8005  * @class
8006  * Create a new date formatter instance. The date formatter is immutable once
8007  * it is created, but can format as many different dates as needed with the same
8008  * options. Create different date formatter instances for different purposes
8009  * and then keep them cached for use later if you have more than one date to
8010  * format.<p>
8011  * 
8012  * The options may contain any of the following properties:
8013  * 
8014  * <ul>
8015  * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is
8016  * not specified, then the default locale of the app or web page will be used.
8017  * 
8018  * <li><i>calendar</i> - the type of calendar to use for this format. The value should
8019  * be a sting containing the name of the calendar. Currently, the supported
8020  * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the
8021  * calendar is not specified, then the default calendar for the locale is used. When the
8022  * calendar type is specified, then the format method must be called with an instance of
8023  * the appropriate date type. (eg. Gregorian calendar means that the format method must 
8024  * be called with a GregDate instance.)
8025  *  
8026  * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone
8027  * instance or a time zone specifier from the IANA list of time zone database names 
8028  * (eg. "America/Los_Angeles"), 
8029  * the string "local", or a string specifying the offset in RFC 822 format. The IANA
8030  * list of time zone names can be viewed at 
8031  * <a href="http://en.wikipedia.org/wiki/List_of_tz_database_time_zones">this page</a>.
8032  * If the time zone is given as "local", the offset from UTC as given by
8033  * the Javascript system is used. If the offset is given as an RFC 822 style offset
8034  * specifier, it will parse that string and use the resulting offset. If the time zone
8035  * is not specified, the
8036  * default time zone for the locale is used. If both the date object and this formatter
8037  * instance contain time zones and those time zones are different from each other, the 
8038  * formatter will calculate the offset between the time zones and subtract it from the 
8039  * date before formatting the result for the current time zone. The theory is that a date
8040  * object that contains a time zone specifies a specific instant in time that is valid
8041  * around the world, whereas a date object without one is a local time and can only be
8042  * used for doing things in the local time zone of the user.
8043  * 
8044  * <li><i>type</i> - Specify whether this formatter should format times only, dates only, or
8045  * both times and dates together. Valid values are "time", "date", and "datetime". Note that
8046  * in some locales, the standard format uses the order "time followed by date" and in others, 
8047  * the order is exactly opposite, so it is better to create a single "datetime" formatter 
8048  * than it is to create a time formatter and a date formatter separately and concatenate the 
8049  * results. A "datetime" formatter will get the order correct for the locale.<p>
8050  * 
8051  * The default type if none is specified in with the type option is "date".
8052  * 
8053  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 
8054  * formatted string.
8055  * 
8056  * <ul>
8057  * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale.
8058  * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format.
8059  * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 
8060  * components may still be abbreviated
8061  * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 
8062  * components are spelled out completely
8063  * </ul>
8064  * 
8065  * eg. The "short" format for an en_US date may be "MM/dd/yy", whereas the long format might be "d MMM, yyyy". In the long
8066  * format, the month name is textual instead of numeric and is longer, the year is 4 digits instead of 2, and the format 
8067  * contains slightly more spaces and formatting characters.<p>
8068  * 
8069  * Note that the length parameter does not specify which components are to be formatted. Use the "date" and the "time"
8070  * properties to specify the components. Also, very few of the components of a time format differ according to the length,
8071  * so this property has little to no affect on time formatting.
8072  * 
8073  * <li><i>date</i> - This property tells
8074  * which components of a date format to use. For example,
8075  * sometimes you may wish to format a date that only contains the month and date
8076  * without the year, such as when displaying a person's yearly birthday. The value
8077  * of this property allows you to specify only those components you want to see in the
8078  * final output, ordered correctly for the locale. <p>
8079  * 
8080  * Valid values are:
8081  * 
8082  * <ul>
8083  * <li><i>dmwy</i> - format all components, weekday, date, month, and year
8084  * <li><i>dmy</i> - format only date, month, and year
8085  * <li><i>dmw</i> - format only weekday, date, and month
8086  * <li><i>dm</i> - format only date and month
8087  * <li><i>my</i> - format only month and year
8088  * <li><i>dw</i> - format only the weekday and date
8089  * <li><i>d</i> - format only the date
8090  * <li><i>m</i> - format only the month, in numbers for shorter lengths, and letters for 
8091  * longer lengths
8092  * <li><i>n</i> - format only the month, in letters only for all lengths
8093  * <li><i>y</i> - format only the year
8094  * </ul>
8095  * Default components, if this property is not specified, is "dmy". This property may be specified
8096  * but has no affect if the current formatter is for times only.<p>
8097  * 
8098  * As of ilib 12.0, you can now pass ICU style skeletons in this option similar to the ones you 
8099  * get from <a href="http://icu-project.org/apiref/icu4c432/classDateTimePatternGenerator.html#aa30c251609c1eea5ad60c95fc497251e">DateTimePatternGenerator.getSkeleton()</a>. 
8100  * It will not extract the length from the skeleton so you still need to pass the length property, 
8101  * but it will extract the date components.
8102  * 
8103  * <li><i>time</i> - This property gives which components of a time format to use. The time will be formatted 
8104  * correctly for the locale with only the time components requested. For example, a clock might only display 
8105  * the hour and minute and not need the seconds or the am/pm component. In this case, the time property should be set 
8106  * to "hm". <p>
8107  * 
8108  * Valid values for this property are:
8109  * 
8110  * <ul>
8111  * <li><i>ahmsz</i> - format the hours, minutes, seconds, am/pm (if using a 12 hour clock), and the time zone
8112  * <li><i>ahms</i> - format the hours, minutes, seconds, and am/pm (if using a 12 hour clock)
8113  * <li><i>hmsz</i> - format the hours, minutes, seconds, and the time zone
8114  * <li><i>hms</i> - format the hours, minutes, and seconds
8115  * <li><i>ahmz</i> - format the hours, minutes, am/pm (if using a 12 hour clock), and the time zone
8116  * <li><i>ahm</i> - format the hours, minutes, and am/pm (if using a 12 hour clock)
8117  * <li><i>hmz</i> - format the hours, minutes, and the time zone
8118  * <li><i>ah</i> - format only the hours and am/pm if using a 12 hour clock
8119  * <li><i>hm</i> - format only the hours and minutes
8120  * <li><i>ms</i> - format only the minutes and seconds
8121  * <li><i>h</i> - format only the hours
8122  * <li><i>m</i> - format only the minutes
8123  * <li><i>s</i> - format only the seconds
8124  * </ul>
8125  * 
8126  * If you want to format a length of time instead of a particular instant
8127  * in time, use the duration formatter object (DurationFmt) instead because this
8128  * formatter is geared towards instants. A date formatter will make sure that each component of the 
8129  * time is within the normal range
8130  * for that component. That is, the minutes will always be between 0 and 59, no matter
8131  * what is specified in the date to format. A duration format will allow the number
8132  * of minutes to exceed 59 if, for example, you were displaying the length of
8133  * a movie of 198 minutes.<p>
8134  * 
8135  * Default value if this property is not specified is "hma".<p>
8136  * 
8137  * As of ilib 12.0, you can now pass ICU style skeletons in this option similar to the ones you 
8138  * get from <a href="http://icu-project.org/apiref/icu4c432/classDateTimePatternGenerator.html#aa30c251609c1eea5ad60c95fc497251e">DateTimePatternGenerator.getSkeleton()</a>. 
8139  * It will not extract the length from the skeleton so you still need to pass the length property, 
8140  * but it will extract the time components.
8141  * 
8142  * <li><i>clock</i> - specify that the time formatter should use a 12 or 24 hour clock. 
8143  * Valid values are "12" and "24".<p>
8144  * 
8145  * In some locales, both clocks are used. For example, in en_US, the general populace uses
8146  * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 
8147  * scientific writing, it is more common to use a 24 hour clock. This property allows you to
8148  * construct a formatter that overrides the default for the locale.<p>
8149  * 
8150  * If this property is not specified, the default is to use the most widely used convention
8151  * for the locale.
8152  *  
8153  * <li><i>template</i> - use the given template string as a fixed format when formatting 
8154  * the date/time. Valid codes to use in a template string are as follows:
8155  * 
8156  * <ul>
8157  * <li><i>a</i> - am/pm marker
8158  * <li><i>d</i> - 1 or 2 digit date of month, not padded
8159  * <li><i>dd</i> - 1 or 2 digit date of month, 0 padded to 2 digits
8160  * <li><i>O</i> - ordinal representation of the date of month (eg. "1st", "2nd", etc.)
8161  * <li><i>D</i> - 1 to 3 digit day of year
8162  * <li><i>DD</i> - 1 to 3 digit day of year, 0 padded to 2 digits
8163  * <li><i>DDD</i> - 1 to 3 digit day of year, 0 padded to 3 digits
8164  * <li><i>M</i> - 1 or 2 digit month number, not padded
8165  * <li><i>MM</i> - 1 or 2 digit month number, 0 padded to 2 digits
8166  * <li><i>N</i> - 1 character month name abbreviation
8167  * <li><i>NN</i> - 2 character month name abbreviation
8168  * <li><i>MMM</i> - 3 character month month name abbreviation
8169  * <li><i>MMMM</i> - fully spelled out month name
8170  * <li><i>yy</i> - 2 digit year
8171  * <li><i>yyyy</i> - 4 digit year
8172  * <li><i>E</i> - day-of-week name, abbreviated to a single character
8173  * <li><i>EE</i> - day-of-week name, abbreviated to a max of 2 characters
8174  * <li><i>EEE</i> - day-of-week name, abbreviated to a max of 3 characters
8175  * <li><i>EEEE</i> - day-of-week name fully spelled out 
8176  * <li><i>G</i> - era designator
8177  * <li><i>w</i> - week number in year
8178  * <li><i>ww</i> - week number in year, 0 padded to 2 digits
8179  * <li><i>W</i> - week in month
8180  * <li><i>h</i> - hour (12 followed by 1 to 11)
8181  * <li><i>hh</i> - hour (12, followed by 1 to 11), 0 padded to 2 digits
8182  * <li><i>k</i> - hour (1 to 24)
8183  * <li><i>kk</i> - hour (1 to 24), 0 padded to 2 digits
8184  * <li><i>H</i> - hour (0 to 23)
8185  * <li><i>HH</i> - hour (0 to 23), 0 padded to 2 digits
8186  * <li><i>K</i> - hour (0 to 11)
8187  * <li><i>KK</i> - hour (0 to 11), 0 padded to 2 digits
8188  * <li><i>m</i> - minute in hour
8189  * <li><i>mm</i> - minute in hour, 0 padded to 2 digits
8190  * <li><i>s</i> - second in minute
8191  * <li><i>ss</i> - second in minute, 0 padded to 2 digits
8192  * <li><i>S</i> - millisecond (1 to 3 digits)
8193  * <li><i>SSS</i> - millisecond, 0 padded to 3 digits
8194  * <li><i>z</i> - general time zone
8195  * <li><i>Z</i> - RFC 822 time zone
8196  * </ul>
8197  * 
8198  * <li><i>useNative</i> - the flag used to determine whether to use the native script settings 
8199  * for formatting the numbers.
8200  *
8201  * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 
8202  * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 
8203  * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 
8204  * (For almost all locales, the Gregorian AM/PM style is most frequently used.)
8205  * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 
8206  * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 
8207  * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian"
8208  * when formatting dates in the Gregorian calendar.
8209  *
8210  * <li><i>onLoad</i> - a callback function to call when the date format object is fully 
8211  * loaded. When the onLoad option is given, the DateFmt object will attempt to
8212  * load any missing locale data using the ilib loader callback.
8213  * When the constructor is done (even if the data is already preassembled), the 
8214  * onLoad function is called with the current instance as a parameter, so this
8215  * callback can be used with preassembled or dynamic loading or a mix of the two.
8216  * 
8217  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
8218  * asynchronously. If this option is given as "false", then the "onLoad"
8219  * callback must be given, as the instance returned from this constructor will
8220  * not be usable for a while.
8221  *  
8222  * <li><i>loadParams</i> - an object containing parameters to pass to the 
8223  * loader callback function when locale data is missing. The parameters are not
8224  * interpretted or modified in any way. They are simply passed along. The object 
8225  * may contain any property/value pairs as long as the calling code is in
8226  * agreement with the loader callback function as to what those parameters mean.
8227  * </ul>
8228  * 
8229  * Any substring containing letters within single or double quotes will be used 
8230  * as-is in the final output and will not be interpretted for codes as above.<p>
8231  * 
8232  * Example: a date format in Spanish might be given as: "'El' d. 'de' MMMM", where
8233  * the 'El' and the 'de' are left as-is in the output because they are quoted. Typical 
8234  * output for this example template might be, "El 5. de Mayo".
8235  * 
8236  * The following options will be used when formatting a date/time with an explicit
8237  * template:
8238  * 
8239  * <ul>
8240  * <li>locale - the locale is only used for 
8241  * translations of things like month names or day-of-week names.
8242  * <li>calendar - used to translate a date instance into date/time component values 
8243  * that can be formatted into the template
8244  * <li>timezone - used to figure out the offset to add or subtract from the time to
8245  * get the final time component values
8246  * <li>clock - used to figure out whether to format times with a 12 or 24 hour clock.
8247  * If this option is specified, it will override the hours portion of a time format.
8248  * That is, "hh" is switched with "HH" and "kk" is switched with "KK" as appropriate. 
8249  * If this option is not specified, the 12/24 code in the template will dictate whether 
8250  * to use the 12 or 24 clock, and the 12/24 default in the locale will be ignored.
8251  * </ul>
8252  * 
8253  * All other options will be ignored and their corresponding getter methods will
8254  * return the empty string.<p>
8255  * 
8256  * 
8257  * @constructor
8258  * @param {Object} options options governing the way this date formatter instance works
8259  */
8260 var DateFmt = function(options) {
8261 	var arr, i, bad, c, comps,
8262 		sync = true, 
8263 		loadParams = undefined;
8264 	
8265 	this.locale = new Locale();
8266 	this.type = "date";
8267 	this.length = "s";
8268 	this.dateComponents = "dmy";
8269 	this.timeComponents = "ahm";
8270 	this.meridiems = "default";
8271 	
8272 	options = options || {sync: true};
8273 	if (options.locale) {
8274 	    this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
8275 	}
8276 
8277 	if (options.type) {
8278 	    if (options.type === 'date' || options.type === 'time' || options.type === 'datetime') {
8279 	        this.type = options.type;
8280 	    }
8281 	}
8282 
8283 	if (options.calendar) {
8284 	    this.calName = options.calendar;
8285 	}
8286 
8287 	if (options.length) {
8288 	    if (options.length === 'short' ||
8289 	        options.length === 'medium' ||
8290 	        options.length === 'long' ||
8291 	        options.length === 'full') {
8292 	        // only use the first char to save space in the json files
8293 	        this.length = options.length.charAt(0);
8294 	    }
8295 	}
8296 
8297 	if (options.date) {
8298 	    arr = options.date.split("");
8299 	    var dateComps = new ISet();
8300 	    bad = false;
8301 	    for (i = 0; i < arr.length; i++) {
8302 	        c = arr[i].toLowerCase();
8303 	        if (c === "e") c = "w"; // map ICU -> ilib
8304 	        if (c !== 'd' && c !== 'm' && c !== 'y' && c !== 'w' && c !== 'n') {
8305 	            // ignore time components and the era
8306 	            if (c !== 'h' && c !== 'm'  && c !== 's' && c !== 'a' && c !== 'z' && c !== 'g') {
8307 	                bad = true;
8308 	                break;
8309 	            }
8310 	        } else {
8311 	            dateComps.add(c);
8312 	        }
8313 	    }
8314 	    if (!bad) {
8315 	        comps = dateComps.asArray().sort(function (left, right) {
8316 	            return (left < right) ? -1 : ((right < left) ? 1 : 0);
8317 	        });
8318 	        this.dateComponents = comps.join("");
8319 	    }
8320 	}
8321 
8322 	if (options.time) {
8323 	    arr = options.time.split("");
8324 	    var timeComps = new ISet();
8325 	    this.badTime = false;
8326 	    for (i = 0; i < arr.length; i++) {
8327 	        c = arr[i].toLowerCase();
8328 	        if (c !== 'h' && c !== 'm' && c !== 's' && c !== 'a' && c !== 'z') {
8329 	            // ignore the date components
8330 	            if (c !== 'd' && c !== 'm' && c !== 'y' && c !== 'w' && c !== 'e' && c !== 'n' && c !== 'g') {
8331 	                this.badTime = true;
8332 	                break;
8333 	            }
8334 	        } else {
8335 	            timeComps.add(c);
8336 	        }
8337 	    }
8338 	    if (!this.badTime) {
8339 	        comps = timeComps.asArray().sort(function (left, right) {
8340 	            return (left < right) ? -1 : ((right < left) ? 1 : 0);
8341 	        });
8342 	        this.timeComponents = comps.join("");
8343 	    }
8344 	}
8345 
8346 	if (options.clock && (options.clock === '12' || options.clock === '24')) {
8347 	    this.clock = options.clock;
8348 	}
8349 
8350 	if (options.template) {
8351 	    // many options are not useful when specifying the template directly, so zero
8352 	    // them out.
8353 	    this.type = "";
8354 	    this.length = "";
8355 	    this.dateComponents = "";
8356 	    this.timeComponents = "";
8357 
8358 	    this.template = options.template;
8359 	}
8360 
8361 	if (options.timezone) {
8362 	    if (options.timezone instanceof TimeZone) {
8363 	        this.tz = options.timezone;
8364 	        this.timezone = this.tz.getId();
8365 	    } else {
8366 	        this.timezone = options.timezone;
8367 	    }
8368 	}
8369 
8370 	if (typeof(options.useNative) === 'boolean') {
8371 	    this.useNative = options.useNative;
8372 	}
8373 
8374 	if (typeof(options.meridiems) !== 'undefined' && 
8375 	    (options.meridiems === "chinese" || 
8376 	        options.meridiems === "gregorian" || 
8377 	        options.meridiems === "ethiopic")) {
8378 	    this.meridiems = options.meridiems;
8379 	}
8380 
8381 	if (typeof(options.sync) !== 'undefined') {
8382 	    sync = (options.sync === true);
8383 	}
8384 
8385 	loadParams = options.loadParams;
8386 
8387 	new LocaleInfo(this.locale, {
8388 		sync: sync,
8389 		loadParams: loadParams, 
8390 		onLoad: ilib.bind(this, function (li) {
8391 			this.locinfo = li;
8392 			
8393 			// get the default calendar name from the locale, and if the locale doesn't define
8394 			// one, use the hard-coded gregorian as the last resort
8395 			this.calName = this.calName || this.locinfo.getCalendar() || "gregorian";
8396 			if (ilib.isDynCode()) {
8397 				// If we are running in the dynamic code loading assembly of ilib, the following
8398 				// will attempt to dynamically load the calendar date class for this calendar. If 
8399 				// it doesn't work, this just goes on and it will use Gregorian instead.
8400 				DateFactory._dynLoadDate(this.calName);
8401 			}
8402 			
8403 			CalendarFactory({
8404 				type: this.calName,
8405 				sync: sync,
8406 				loadParams: loadParams,
8407 				onLoad: ilib.bind(this, function(cal) {
8408 				    this.cal = cal;
8409 				    
8410 				    if (!this.cal) {
8411 				        // can be synchronous
8412 				        this.cal = new GregorianCal();
8413 				    }
8414 				    if (this.meridiems === "default") {
8415 				        this.meridiems = li.getMeridiemsStyle();
8416 				    }
8417 
8418 				    // load the strings used to translate the components
8419 				    new ResBundle({
8420 				        locale: this.locale,
8421 				        name: "sysres",
8422 				        sync: sync,
8423 				        loadParams: loadParams, 
8424 				        onLoad: ilib.bind(this, function (rb) {
8425 				            this.sysres = rb;
8426 				            
8427 				            if (!this.tz) {
8428 				                var timezone = options.timezone;
8429 				                if (!timezone && !options.locale) { 
8430 				                    timezone = "local";
8431 				                }
8432 				                
8433 				                new TimeZone({
8434 				                    locale: this.locale,
8435 				                    id: timezone,
8436 				                    sync: sync,
8437 				                    loadParams: loadParams,
8438 				                    onLoad: ilib.bind(this, function(tz) {
8439 				                        this.tz = tz;
8440 				                        this._init(options);
8441 				                    })
8442 				                });
8443 				            } else {
8444 				                this._init(options);
8445 				            }
8446 				        })
8447 				    });
8448 				})
8449 			});
8450 		})
8451 	});
8452 };
8453 
8454 // used in getLength
8455 DateFmt.lenmap = {
8456 	"s": "short",
8457 	"m": "medium",
8458 	"l": "long",
8459 	"f": "full"
8460 };
8461 
8462 DateFmt.defaultFmt = {
8463 	"gregorian": {
8464 		"order": "{date} {time}",
8465 		"date": {
8466 			"dmwy": "EEE d/MM/yyyy",
8467 			"dmy": "d/MM/yyyy",
8468 			"dmw": "EEE d/MM",
8469 			"dm": "d/MM",
8470 			"my": "MM/yyyy",
8471 			"dw": "EEE d",
8472 			"d": "dd",
8473 			"m": "MM",
8474 			"y": "yyyy",
8475 			"n": "NN",
8476 			"w": "EEE"
8477 		},
8478 		"time": {
8479 			"12": "h:mm:ssa",
8480 			"24": "H:mm:ss"
8481 		},
8482 		"range": {
8483 			"c00": "{st} - {et}, {sd}/{sm}/{sy}",
8484 			"c01": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}",
8485 			"c02": "{sd}/{sm} {st} - {ed}/{em} {et}, {sy}",
8486 			"c03": "{sd}/{sm}/{sy} {st} - {ed}/{em}/{ey} {et}",
8487 			"c10": "{sd}-{ed}/{sm}/{sy}",
8488 			"c11": "{sd}/{sm} - {ed}/{em} {sy}",
8489 			"c12": "{sd}/{sm}/{sy} - {ed}/{em}/{ey}",
8490 			"c20": "{sm}/{sy} - {em}/{ey}",
8491 			"c30": "{sy} - {ey}"
8492 		}
8493 	},
8494 	"islamic": "gregorian",
8495 	"hebrew": "gregorian",
8496 	"julian": "gregorian",
8497 	"buddhist": "gregorian",
8498 	"persian": "gregorian",
8499 	"persian-algo": "gregorian",
8500 	"han": "gregorian"
8501 };
8502 
8503 /**
8504 * @static
8505 * @private
8506 */
8507 DateFmt.monthNameLenMap = {
8508 	"short" : "N",
8509 	"medium": "NN",
8510 	"long":   "MMM",
8511 	"full":   "MMMM"
8512 };
8513 
8514 /**
8515 * @static
8516 * @private
8517 */
8518 DateFmt.weekDayLenMap = {
8519 	"short" : "E",
8520 	"medium": "EE",
8521 	"long":   "EEE",
8522 	"full":   "EEEE"
8523 };
8524 
8525 /**
8526  * Return the range of possible meridiems (times of day like "AM" or 
8527  * "PM") in this date formatter.<p>
8528  *
8529  * The options may contain any of the following properties:
8530  *
8531  * <ul>
8532  * <li><i>locale</i> - locale to use when formatting the date/time. If the locale is
8533  * not specified, then the default locale of the app or web page will be used.
8534  * 
8535  * <li><i>meridiems</i> - string that specifies what style of meridiems to use with this 
8536  * format. The choices are "default", "gregorian", "ethiopic", and "chinese". The "default" 
8537  * style is often the simple Gregorian AM/PM, but the actual style is chosen by the locale. 
8538  * (For almost all locales, the Gregorian AM/PM style is most frequently used.)
8539  * The "ethiopic" style uses 5 different meridiems for "morning", "noon", "afternoon", 
8540  * "evening", and "night". The "chinese" style uses 7 different meridiems corresponding 
8541  * to the various parts of the day. N.B. Even for the Chinese locales, the default is "gregorian"
8542  * when formatting dates in the Gregorian calendar.
8543  * </ul>
8544  *
8545  * @static
8546  * @public
8547  * @param {Object} options options governing the way this date formatter instance works for getting meridiems range
8548  * @return {Array.<{name:string,start:string,end:string}>}
8549  */
8550 DateFmt.getMeridiemsRange = function (options) {
8551 	options = options || {sync: true};
8552 	var args = JSUtils.merge({}, options);
8553 	args.onLoad = function(fmt) {
8554 	    if (typeof(options.onLoad) === "function") {
8555 	        options.onLoad(fmt.getMeridiemsRange());
8556 	    }
8557 	};
8558 	var fmt = new DateFmt(args);
8559 
8560 	return fmt.getMeridiemsRange();
8561 };
8562 
8563 DateFmt.prototype = {
8564     /**
8565      * @private
8566      * Finish initializing the formatter object
8567      */
8568     _init: function(options) {
8569         if (typeof (options.sync) === 'undefined') {
8570             options.sync = true;
8571         }
8572         if (!this.template) {
8573             Utils.loadData({
8574                 object: "DateFmt", 
8575                 locale: this.locale, 
8576                 name: "dateformats.json", 
8577                 sync: options.sync, 
8578                 loadParams: options.loadParams, 
8579                 callback: ilib.bind(this, function (formats) {
8580                     var spec = this.locale.getSpec().replace(/-/g, '_');
8581                     if (!formats) {
8582                         formats = ilib.data.dateformats || DateFmt.defaultFmt;
8583                         ilib.data.cache.DateFmt[spec] = formats;
8584                     }
8585                     
8586                     if (typeof(this.clock) === 'undefined') {
8587                         // default to the locale instead
8588                         this.clock = this.locinfo.getClock();
8589                     }
8590                     
8591                     var ret = this;
8592 
8593                     if (typeof(options.sync) === "boolean" && !options.sync) {
8594                         // in async mode, capture the exception and call the callback with "undefined"
8595                         try {
8596                             this._initTemplate(formats);
8597                             this._massageTemplate();
8598                         } catch (e) {
8599                             ret = undefined;
8600                         }
8601                     } else {
8602                         // in sync mode, allow the exception to percolate upwards
8603                         this._initTemplate(formats);
8604                         this._massageTemplate();
8605                     }
8606 
8607                     if (typeof(options.onLoad) === 'function') {
8608                         options.onLoad(ret);
8609                     }
8610                })
8611             });
8612         } else {
8613             this._massageTemplate();
8614         
8615             if (typeof(options.onLoad) === 'function') {
8616                 options.onLoad(this);
8617             }
8618         }
8619     },
8620     
8621 	/**
8622 	 * @protected
8623 	 * @param {string|{
8624 	 * 		order:(string|{
8625 	 * 			s:string,
8626 	 * 			m:string,
8627 	 * 			l:string,
8628 	 * 			f:string
8629 	 * 		}),
8630 	 * 		date:Object.<string, (string|{
8631 	 * 			s:string,
8632 	 * 			m:string,
8633 	 * 			l:string,
8634 	 * 			f:string
8635 	 * 		})>,
8636 	 * 		time:Object.<string,Object.<string,(string|{
8637 	 * 			s:string,
8638 	 * 			m:string,
8639 	 * 			l:string,
8640 	 * 			f:string
8641 	 * 		})>>,
8642 	 * 		range:Object.<string, (string|{
8643 	 * 			s:string,
8644 	 * 			m:string,
8645 	 * 			l:string,
8646 	 * 			f:string
8647 	 * 		})>
8648 	 * 	}} formats
8649 	 */
8650 	_initTemplate: function (formats) {
8651 		if (formats[this.calName]) {
8652 			var name = formats[this.calName];
8653 			// may be an alias to another calendar type
8654 			this.formats = (typeof(name) === "string") ? formats[name] : name;
8655 			
8656 			this.template = "";
8657 			
8658 			switch (this.type) {
8659 				case "datetime":
8660 					this.template = (this.formats && this._getLengthFormat(this.formats.order, this.length)) || "{date} {time}";
8661 					this.template = this.template.replace("{date}", this._getFormat(this.formats.date, this.dateComponents, this.length) || "");
8662 					this.template = this.template.replace("{time}", this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length) || "");
8663 					break;
8664 				case "date":
8665 					this.template = this._getFormat(this.formats.date, this.dateComponents, this.length);
8666 					break;
8667 				case "time":
8668 					this.template = this._getFormat(this.formats.time[this.clock], this.timeComponents, this.length);
8669 					break;
8670 			}
8671 			
8672 			// calculate what order the components appear in for this locale
8673 			this.componentOrder = this._getFormat(this.formats.date, "dmy", "l").
8674 			    replace(/[^dMy]/g, "").
8675 			    replace(/y+/, "y").
8676 			    replace(/d+/, "d").
8677 			    replace(/M+/, "m");
8678 		} else {
8679 			throw "No formats available for calendar " + this.calName + " in locale " + this.locale.toString();
8680 		}
8681 	},
8682 	
8683 	/**
8684 	 * @protected
8685 	 */
8686 	_massageTemplate: function () {
8687 		var i;
8688 		
8689 		if (this.clock && this.template) {
8690 			// explicitly set the hours to the requested type
8691 			var temp = "";
8692 			switch (this.clock) {
8693 				case "24":
8694 					for (i = 0; i < this.template.length; i++) {
8695 						if (this.template.charAt(i) == "'") {
8696 							temp += this.template.charAt(i++);
8697 							while (i < this.template.length && this.template.charAt(i) !== "'") {
8698 								temp += this.template.charAt(i++);
8699 							}
8700 							if (i < this.template.length) {
8701 								temp += this.template.charAt(i);
8702 							}
8703 						} else if (this.template.charAt(i) == 'K') {
8704 							temp += 'k';
8705 						} else if (this.template.charAt(i) == 'h') {
8706 							temp += 'H';
8707 						} else {
8708 							temp += this.template.charAt(i);
8709 						}
8710 					}
8711 					this.template = temp;
8712 					break;
8713 				case "12":
8714 					for (i = 0; i < this.template.length; i++) {
8715 						if (this.template.charAt(i) == "'") {
8716 							temp += this.template.charAt(i++);
8717 							while (i < this.template.length && this.template.charAt(i) !== "'") {
8718 								temp += this.template.charAt(i++);
8719 							}
8720 							if (i < this.template.length) {
8721 								temp += this.template.charAt(i);
8722 							}
8723 						} else if (this.template.charAt(i) == 'k') {
8724 							temp += 'K';
8725 						} else if (this.template.charAt(i) == 'H') {
8726 							temp += 'h';
8727 						} else {
8728 							temp += this.template.charAt(i);
8729 						}
8730 					}
8731 					this.template = temp;
8732 					break;
8733 			}
8734 		}
8735 		
8736 		// tokenize it now for easy formatting
8737 		this.templateArr = this._tokenize(this.template);
8738 
8739 		var digits;
8740 		// set up the mapping to native or alternate digits if necessary
8741 		if (typeof(this.useNative) === "boolean") {
8742 			if (this.useNative) {
8743 				digits = this.locinfo.getNativeDigits();
8744 				if (digits) {
8745 					this.digits = digits;
8746 				}
8747 			}
8748 		} else if (this.locinfo.getDigitsStyle() === "native") {
8749 			digits = this.locinfo.getNativeDigits();
8750 			if (digits) {
8751 				this.useNative = true;
8752 				this.digits = digits;
8753 			}
8754 		}
8755 	},
8756     
8757 	/**
8758 	 * Convert the template into an array of date components separated by formatting chars.
8759 	 * @protected
8760 	 * @param {string} template Format template to tokenize into components
8761 	 * @return {Array.<string>} a tokenized array of date format components
8762 	 */
8763 	_tokenize: function (template) {
8764 		var i = 0, start, ch, letter, arr = [];
8765 		
8766 		// console.log("_tokenize: tokenizing template " + template);
8767 		if (template) {
8768 			while (i < template.length) {
8769 				ch = template.charAt(i);
8770 				start = i;
8771 				if (ch === "'") {
8772 					// console.log("found quoted string");
8773 					i++;
8774 					// escaped string - push as-is, then dequote later
8775 					while (i < template.length && template.charAt(i) !== "'") {
8776 						i++;
8777 					}
8778 					if (i < template.length) {
8779 						i++;	// grab the other quote too
8780 					}
8781 				} else if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) {
8782 					letter = template.charAt(i);
8783 					// console.log("found letters " + letter);
8784 					while (i < template.length && ch === letter) {
8785 						ch = template.charAt(++i);
8786 					}
8787 				} else {
8788 					// console.log("found other");
8789 					while (i < template.length && ch !== "'" && (ch < 'a' || ch > 'z') && (ch < 'A' || ch > 'Z')) {
8790 						ch = template.charAt(++i);
8791 					}
8792 				}
8793 				arr.push(template.substring(start,i));
8794 				// console.log("start is " + start + " i is " + i + " and substr is " + template.substring(start,i));
8795 			}
8796 		}
8797 		return arr;
8798 	},
8799     
8800 	/**
8801 	 * @protected
8802 	 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search
8803 	 * @param {string} components Format components to search
8804 	 * @param {string} length Length of the requested format
8805 	 * @return {string|undefined} the requested format
8806 	 */
8807 	_getFormatInternal: function getFormatInternal(obj, components, length) {
8808 		if (typeof(components) !== 'undefined' && obj && obj[components]) {
8809 			return this._getLengthFormat(obj[components], length);
8810 		}
8811 		return undefined;
8812 	},
8813 
8814 	// stand-alone of m (month) is l
8815 	// stand-alone of d (day) is a
8816 	// stand-alone of w (weekday) is e
8817 	// stand-alone of y (year) is r
8818 	_standAlones: {
8819 		"m": "l",
8820 		"d": "a",
8821 		"w": "e",
8822 		"y": "r"
8823 	},
8824 	
8825 	/**
8826 	 * @protected
8827 	 * @param {Object.<string, (string|{s:string,m:string,l:string,f:string})>} obj Object to search
8828 	 * @param {string} components Format components to search
8829 	 * @param {string} length Length of the requested format
8830 	 * @return {string|undefined} the requested format
8831 	 */
8832 	_getFormat: function getFormat(obj, components, length) {
8833 		// handle some special cases for stand-alone formats
8834 		if (components && this._standAlones[components]) {
8835     		var tmp = this._getFormatInternal(obj, this._standAlones[components], length);
8836     		if (tmp) {
8837     			return tmp;
8838     		}
8839 		}
8840 		
8841 		// if no stand-alone format is available, fall back to the regular format
8842 		return this._getFormatInternal(obj, components, length);
8843 	},
8844 
8845 	/**
8846 	 * @protected
8847 	 * @param {(string|{s:string,m:string,l:string,f:string})} obj Object to search
8848 	 * @param {string} length Length of the requested format
8849 	 * @return {(string|undefined)} the requested format
8850 	 */
8851 	_getLengthFormat: function getLengthFormat(obj, length) {
8852 		if (typeof(obj) === 'string') {
8853 			return obj;
8854 		} else if (obj[length]) {
8855 			return obj[length];
8856 		}
8857 		return undefined;
8858 	},
8859 
8860 	/**
8861 	 * Return the locale used with this formatter instance.
8862 	 * @return {Locale} the Locale instance for this formatter
8863 	 */
8864 	getLocale: function() {
8865 		return this.locale;
8866 	},
8867 	
8868 	/**
8869 	 * Return the template string that is used to format date/times for this
8870 	 * formatter instance. This will work, even when the template property is not explicitly 
8871 	 * given in the options to the constructor. Without the template option, the constructor 
8872 	 * will build the appropriate template according to the options and use that template
8873 	 * in the format method. 
8874 	 * 
8875 	 * @return {string} the format template for this formatter
8876 	 */
8877 	getTemplate: function() {
8878 		return this.template;
8879 	},
8880 	
8881 	/**
8882 	 * Return the order of the year, month, and date components for the current locale.<p>
8883 	 * 
8884 	 * When implementing a date input widget in a UI, it would be useful to know what
8885 	 * order to put the year, month, and date input fields so that it conforms to the
8886 	 * user expectations for the locale. This method gives that order by returning a
8887 	 * string that has a single "y", "m", and "d" character in it in the correct
8888 	 * order.<p>
8889 	 * 
8890 	 * For example, the return value "ymd" means that this locale formats the year first,
8891 	 * the month second, and the date third, and "mdy" means that the month is first,
8892 	 * the date is second, and the year is third. Four of the 6 possible permutations
8893 	 * of the three letters have at least one locale that uses that ordering, though some
8894 	 * combinations are far more likely than others. The ones that are not used by any 
8895 	 * locales are "dym" and "myd", though new locales are still being added to 
8896 	 * CLDR frequently, and possible orderings cannot be predicted. Your code should 
8897 	 * support all 6 possibilities, just in case.
8898 	 * 
8899 	 * @return {string} a string giving the date component order
8900 	 */
8901 	getDateComponentOrder: function() {
8902 	    return this.componentOrder;
8903 	},
8904 	
8905 	/**
8906 	 * Return the type of this formatter. The type is a string that has one of the following
8907 	 * values: "time", "date", "datetime".
8908 	 * @return {string} the type of the formatter
8909 	 */
8910 	getType: function() {
8911 		return this.type;
8912 	},
8913 	
8914 	/**
8915 	 * Return the name of the calendar used to format date/times for this
8916 	 * formatter instance.
8917 	 * @return {string} the name of the calendar used by this formatter
8918 	 */
8919 	getCalendar: function () {
8920 		return this.cal.getType();
8921 	},
8922 	
8923 	/**
8924 	 * Return the length used to format date/times in this formatter. This is either the
8925 	 * value of the length option to the constructor, or the default value.
8926 	 * 
8927 	 * @return {string} the length of formats this formatter returns
8928 	 */
8929 	getLength: function () {
8930 		return DateFmt.lenmap[this.length] || "";
8931 	},
8932 	
8933 	/**
8934 	 * Return the date components that this formatter formats. This is either the 
8935 	 * value of the date option to the constructor, or the default value. If this
8936 	 * formatter is a time-only formatter, this method will return the empty 
8937 	 * string. The date component letters may be specified in any order in the 
8938 	 * constructor, but this method will reorder the given components to a standard 
8939 	 * order.
8940 	 * 
8941 	 * @return {string} the date components that this formatter formats
8942 	 */
8943 	getDateComponents: function () {
8944 		return this.dateComponents || "";
8945 	},
8946 
8947 	/**
8948 	 * Return the time components that this formatter formats. This is either the 
8949 	 * value of the time option to the constructor, or the default value. If this
8950 	 * formatter is a date-only formatter, this method will return the empty 
8951 	 * string. The time component letters may be specified in any order in the 
8952 	 * constructor, but this method will reorder the given components to a standard 
8953 	 * order.
8954 	 * 
8955 	 * @return {string} the time components that this formatter formats
8956 	 */
8957 	getTimeComponents: function () {
8958 		return this.timeComponents || "";
8959 	},
8960 
8961 	/**
8962 	 * Return the time zone used to format date/times for this formatter
8963 	 * instance.
8964 	 * @return {TimeZone} a time zone object that this formatter is formatting for
8965 	 */
8966 	getTimeZone: function () {
8967 		return this.tz;
8968 	},
8969 	
8970 	/**
8971 	 * Return the clock option set in the constructor. If the clock option was
8972 	 * not given, the default from the locale is returned instead.
8973 	 * @return {string} "12" or "24" depending on whether this formatter uses
8974 	 * the 12-hour or 24-hour clock
8975 	 */
8976 	getClock: function () {
8977 		return this.clock || this.locinfo.getClock();
8978 	},
8979 	
8980 	/**
8981 	 * Return the meridiems range in current locale. 
8982 	 * @return {Array.<{name:string,start:string,end:string}>} the range of available meridiems
8983 	 */
8984 	getMeridiemsRange: function () {
8985 		var result;
8986 		var _getSysString = function (key) {
8987 			return (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key)).toString();
8988 		};
8989 
8990 		switch (this.meridiems) {
8991 		case "chinese":
8992 			result = [
8993 				{
8994 					name: _getSysString.call(this, "azh0"),
8995 					start: "00:00",
8996 					end: "05:59"
8997 				},
8998 				{
8999 					name: _getSysString.call(this, "azh1"),
9000 					start: "06:00",
9001 					end: "08:59"
9002 				},
9003 				{
9004 					name: _getSysString.call(this, "azh2"),
9005 					start: "09:00",
9006 					end: "11:59"
9007 				},
9008 				{
9009 					name: _getSysString.call(this, "azh3"),
9010 					start: "12:00",
9011 					end: "12:59"
9012 				},
9013 				{
9014 					name: _getSysString.call(this, "azh4"),
9015 					start: "13:00",
9016 					end: "17:59"
9017 				},
9018 				{
9019 					name: _getSysString.call(this, "azh5"),
9020 					start: "18:00",
9021 					end: "20:59"
9022 				},
9023 				{
9024 					name: _getSysString.call(this, "azh6"),
9025 					start: "21:00",
9026 					end: "23:59"
9027 				}
9028 			];
9029 			break;
9030 		case "ethiopic":
9031 			result = [
9032 				{
9033 					name: _getSysString.call(this, "a0-ethiopic"),
9034 					start: "00:00",
9035 					end: "05:59"
9036 				},
9037 				{
9038 					name: _getSysString.call(this, "a1-ethiopic"),
9039 					start: "06:00",
9040 					end: "06:00"
9041 				},
9042 				{
9043 					name: _getSysString.call(this, "a2-ethiopic"),
9044 					start: "06:01",
9045 					end: "11:59"
9046 				},
9047 				{
9048 					name: _getSysString.call(this, "a3-ethiopic"),
9049 					start: "12:00",
9050 					end: "17:59"
9051 				},
9052 				{
9053 					name: _getSysString.call(this, "a4-ethiopic"),
9054 					start: "18:00",
9055 					end: "23:59"
9056 				}
9057 			];
9058 			break;
9059 		default:
9060 			result = [
9061 				{
9062 					name: _getSysString.call(this, "a0"),
9063 					start: "00:00",
9064 					end: "11:59"
9065 				},
9066 				{
9067 					name: _getSysString.call(this, "a1"),
9068 					start: "12:00",
9069 					end: "23:59"
9070 				}
9071 			];
9072 			break;
9073 		}
9074 
9075 		return result;
9076 	},
9077 	
9078 	/**
9079 	 * @private
9080 	 */
9081 	_getTemplate: function (prefix, calendar) {
9082 		if (calendar !== "gregorian") {
9083 			return prefix + "-" + calendar;
9084 		}
9085 		return prefix;
9086 	},
9087 
9088 	/**
9089 	 * Returns an array of the months of the year, formatted to the optional length specified.
9090 	 * i.e. ...getMonthsOfYear() OR ...getMonthsOfYear({length: "short"})
9091 	 * <p>
9092 	 * The options parameter may contain any of the following properties:
9093 	 * 
9094 	 * <ul>
9095 	 * <li><i>length</i> - length of the names of the months being sought. This may be one of
9096 	 * "short", "medium", "long", or "full"
9097 	 * <li><i>date</i> - retrieve the names of the months in the date of the given date
9098 	 * <li><i>year</i> - retrieve the names of the months in the given year. In some calendars,
9099 	 * the months have different names depending if that year is a leap year or not.
9100 	 * </ul>
9101 	 * 
9102 	 * @param  {Object=} options an object-literal that contains any of the above properties
9103 	 * @return {Array} an array of the names of all of the months of the year in the current calendar
9104 	 */
9105 	getMonthsOfYear: function(options) {
9106 		var length = (options && options.length) || this.getLength(),
9107 			template = DateFmt.monthNameLenMap[length],
9108 			months = [undefined],
9109 			date,
9110 			monthCount;
9111 		
9112 		if (options) {
9113 			if (options.date) {
9114 				date = DateFactory._dateToIlib(options.date); 	
9115 			}
9116 			
9117 			if (options.year) {
9118 				date = DateFactory({year: options.year, month: 1, day: 1, type: this.cal.getType()});
9119 			}
9120 		}
9121 		
9122 		if (!date) {
9123 			date = DateFactory({
9124 				calendar: this.cal.getType()
9125 			});
9126 		}
9127 
9128 		monthCount = this.cal.getNumMonths(date.getYears());
9129 		for (var i = 1; i <= monthCount; i++) {
9130 			months[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString();
9131 		}
9132 		return months;
9133 	},
9134 
9135 	/**
9136 	 * Returns an array of the days of the week, formatted to the optional length specified.
9137 	 * i.e. ...getDaysOfWeek() OR ...getDaysOfWeek({length: "short"})
9138 	 * <p>
9139 	 * The options parameter may contain any of the following properties:
9140 	 * 
9141 	 * <ul>
9142 	 * <li><i>length</i> - length of the names of the months being sought. This may be one of
9143 	 * "short", "medium", "long", or "full"
9144 	 * </ul>
9145 	 * @param  {Object=} options an object-literal that contains one key 
9146 	 *                   "length" with the standard length strings
9147 	 * @return {Array} an array of all of the names of the days of the week
9148 	 */
9149 	getDaysOfWeek: function(options) {
9150 		var length = (options && options.length) || this.getLength(),
9151 			template = DateFmt.weekDayLenMap[length],
9152 			days = [];
9153 		for (var i = 0; i < 7; i++) {
9154 			days[i] = this.sysres.getString(this._getTemplate(template + i, this.cal.getType())).toString();
9155 		}
9156 		return days;
9157 	},
9158 
9159 	
9160 	/**
9161 	 * Convert this formatter to a string representation by returning the
9162 	 * format template. This method delegates to getTemplate.
9163 	 * 
9164 	 * @return {string} the format template
9165 	 */
9166 	toString: function() {
9167 		return this.getTemplate();
9168 	},
9169 		
9170 	/**
9171 	 * @private
9172 	 * Format a date according to a sequence of components. 
9173 	 * @param {IDate} date a date/time object to format
9174 	 * @param {Array.<string>} templateArr an array of components to format
9175 	 * @return {string} the formatted date
9176 	 */
9177 	_formatTemplate: function (date, templateArr) {
9178 		var i, key, temp, tz, str = "";
9179 		for (i = 0; i < templateArr.length; i++) {
9180 			switch (templateArr[i]) {
9181 				case 'd':
9182 					str += (date.day || 1);
9183 					break;
9184 				case 'dd':
9185 					str += JSUtils.pad(date.day || "1", 2);
9186 					break;
9187 				case 'yy':
9188 					temp = "" + ((date.year || 0) % 100);
9189 					str += JSUtils.pad(temp, 2);
9190 					break;
9191 				case 'yyyy':
9192 					str += JSUtils.pad(date.year || "0", 4);
9193 					break;
9194 				case 'M':
9195 					str += (date.month || 1);
9196 					break;
9197 				case 'MM':
9198 					str += JSUtils.pad(date.month || "1", 2);
9199 					break;
9200 				case 'h':
9201 					temp = (date.hour || 0) % 12;
9202 					if (temp == 0) {
9203 						temp = "12";
9204 					}
9205 					str += temp; 
9206 					break;
9207 				case 'hh':
9208 					temp = (date.hour || 0) % 12;
9209 					if (temp == 0) {
9210 						temp = "12";
9211 					}
9212 					str += JSUtils.pad(temp, 2);
9213 					break;
9214 				/*
9215 				case 'j':
9216 					temp = (date.hour || 0) % 12 + 1;
9217 					str += temp; 
9218 					break;
9219 				case 'jj':
9220 					temp = (date.hour || 0) % 12 + 1;
9221 					str += JSUtils.pad(temp, 2);
9222 					break;
9223 				*/
9224 				case 'K':
9225 					temp = (date.hour || 0) % 12;
9226 					str += temp; 
9227 					break;
9228 				case 'KK':
9229 					temp = (date.hour || 0) % 12;
9230 					str += JSUtils.pad(temp, 2);
9231 					break;
9232 
9233 				case 'H':
9234 					str += (date.hour || "0");
9235 					break;
9236 				case 'HH':
9237 					str += JSUtils.pad(date.hour || "0", 2);
9238 					break;
9239 				case 'k':
9240 					str += (date.hour == 0 ? "24" : date.hour);
9241 					break;
9242 				case 'kk':
9243 					temp = (date.hour == 0 ? "24" : date.hour);
9244 					str += JSUtils.pad(temp, 2);
9245 					break;
9246 
9247 				case 'm':
9248 					str += (date.minute || "0");
9249 					break;
9250 				case 'mm':
9251 					str += JSUtils.pad(date.minute || "0", 2);
9252 					break;
9253 				case 's':
9254 					str += (date.second || "0");
9255 					break;
9256 				case 'ss':
9257 					str += JSUtils.pad(date.second || "0", 2);
9258 					break;
9259 				case 'S':
9260 					str += (date.millisecond || "0");
9261 					break;
9262 				case 'SSS':
9263 					str += JSUtils.pad(date.millisecond || "0", 3);
9264 					break;
9265 
9266 				case 'N':
9267 				case 'NN':
9268 				case 'MMM':
9269 				case 'MMMM':
9270 				case 'L':
9271 				case 'LL':
9272 				case 'LLL':
9273 				case 'LLLL':
9274 					key = templateArr[i] + (date.month || 1);
9275 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9276 					break;
9277 					
9278 				case 'E':
9279 				case 'EE':
9280 				case 'EEE':
9281 				case 'EEEE':
9282 				case 'c':
9283 				case 'cc':
9284 				case 'ccc':
9285 				case 'cccc':
9286 					key = templateArr[i] + date.getDayOfWeek();
9287 					//console.log("finding " + key + " in the resources");
9288 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9289 					break;
9290 
9291 				case 'a':
9292 					switch (this.meridiems) {
9293 					case "chinese":
9294 						if (date.hour < 6) {
9295 							key = "azh0";	// before dawn
9296 						} else if (date.hour < 9) {
9297 							key = "azh1";	// morning
9298 						} else if (date.hour < 12) {
9299 							key = "azh2";	// late morning/day before noon
9300 						} else if (date.hour < 13) {
9301 							key = "azh3";	// noon hour/midday
9302 						} else if (date.hour < 18) {
9303 							key = "azh4";	// afternoon
9304 						} else if (date.hour < 21) {
9305 							key = "azh5";	// evening time/dusk
9306 						} else {
9307 							key = "azh6";	// night time
9308 						}
9309 						break;
9310 					case "ethiopic":
9311 						if (date.hour < 6) {
9312 							key = "a0-ethiopic";	// morning
9313 						} else if (date.hour === 6 && date.minute === 0) {
9314 							key = "a1-ethiopic";	// noon
9315 						} else if (date.hour >= 6 && date.hour < 12) {
9316 							key = "a2-ethiopic";	// afternoon
9317 						} else if (date.hour >= 12 && date.hour < 18) {
9318 							key = "a3-ethiopic";	// evening
9319 						} else if (date.hour >= 18) {
9320 							key = "a4-ethiopic";	// night
9321 						}
9322 						break;
9323 					default:
9324 						key = date.hour < 12 ? "a0" : "a1";
9325 						break;
9326 					}
9327 					//console.log("finding " + key + " in the resources");
9328 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9329 					break;
9330 					
9331 				case 'w':
9332 					str += date.getWeekOfYear();
9333 					break;
9334 				case 'ww':
9335 					str += JSUtils.pad(date.getWeekOfYear(), 2);
9336 					break;
9337 
9338 				case 'D':
9339 					str += date.getDayOfYear();
9340 					break;
9341 				case 'DD':
9342 					str += JSUtils.pad(date.getDayOfYear(), 2);
9343 					break;
9344 				case 'DDD':
9345 					str += JSUtils.pad(date.getDayOfYear(), 3);
9346 					break;
9347 				case 'W':
9348 					str += date.getWeekOfMonth(this.locale);
9349 					break;
9350 
9351 				case 'G':
9352 					key = "G" + date.getEra();
9353 					str += (this.sysres.getString(undefined, key + "-" + this.calName) || this.sysres.getString(undefined, key));
9354 					break;
9355 
9356 				case 'O':
9357 					temp = this.sysres.getString("1#1st|2#2nd|3#3rd|21#21st|22#22nd|23#23rd|31#31st|#{num}th", "ordinalChoice");
9358 					str += temp.formatChoice(date.day, {num: date.day});
9359 					break;
9360 					
9361 				case 'z': // general time zone
9362 					tz = this.getTimeZone(); // lazy-load the tz
9363 					str += tz.getDisplayName(date, "standard");
9364 					break;
9365 				case 'Z': // RFC 822 time zone
9366 					tz = this.getTimeZone(); // lazy-load the tz
9367 					str += tz.getDisplayName(date, "rfc822");
9368 					break;
9369 
9370 				default:
9371 					str += templateArr[i].replace(/'/g, "");
9372 					break;
9373 			}
9374 		}
9375 
9376 		if (this.digits) {
9377 			str = JSUtils.mapString(str, this.digits);
9378 		}
9379 		return str;
9380 	},
9381 	
9382 	/**
9383 	 * Format a particular date instance according to the settings of this
9384 	 * formatter object. The type of the date instance being formatted must 
9385 	 * correspond exactly to the calendar type with which this formatter was 
9386 	 * constructed. If the types are not compatible, this formatter will
9387 	 * produce bogus results.
9388 	 * 
9389 	 * @param {IDate|number|string|Date|JulianDay|null|undefined} dateLike a date-like object to format
9390 	 * @return {string} the formatted version of the given date instance
9391 	 */
9392 	format: function (dateLike) {
9393 		var thisZoneName = this.tz && this.tz.getId() || "local";
9394 
9395 		var date = DateFactory._dateToIlib(dateLike, thisZoneName, this.locale);
9396 		
9397 		if (!date.getCalendar || !(date instanceof IDate)) {
9398 			throw "Wrong date type passed to DateFmt.format()";
9399 		}
9400 		
9401 		var dateZoneName = date.timezone || "local";
9402 		
9403 		// convert to the time zone of this formatter before formatting
9404 		if (dateZoneName !== thisZoneName || date.getCalendar() !== this.calName) {
9405 			// console.log("Differing time zones date: " + dateZoneName + " and fmt: " + thisZoneName + ". Converting...");
9406 			// this will recalculate the date components based on the new time zone
9407 			// and/or convert a date in another calendar to the current calendar before formatting it
9408 			var newDate = DateFactory({
9409 				type: this.calName,
9410 				timezone: thisZoneName,
9411 				julianday: date.getJulianDay()
9412 			});
9413 			
9414 			date = newDate;
9415 		}
9416 		return this._formatTemplate(date, this.templateArr);
9417 	},
9418 	
9419 	/**
9420 	 * Return a string that describes a date relative to the given 
9421 	 * reference date. The string returned is text that for the locale that
9422 	 * was specified when the formatter instance was constructed.<p>
9423 	 * 
9424 	 * The date can be in the future relative to the reference date or in
9425 	 * the past, and the formatter will generate the appropriate string.<p>
9426 	 * 
9427 	 * The text used to describe the relative reference depends on the length
9428 	 * of time between the date and the reference. If the time was in the
9429 	 * past, it will use the "ago" phrase, and in the future, it will use
9430 	 * the "in" phrase. Examples:<p>
9431 	 * 
9432 	 * <ul>
9433 	 * <li>within a minute: either "X seconds ago" or "in X seconds"
9434 	 * <li>within an hour: either "X minutes ago" or "in X minutes"
9435 	 * <li>within a day: either "X hours ago" or "in X hours"
9436 	 * <li>within 2 weeks: either "X days ago" or "in X days"
9437 	 * <li>within 12 weeks (~3 months): either "X weeks ago" or "in X weeks"
9438 	 * <li>within two years: either "X months ago" or "in X months"
9439 	 * <li>longer than 2 years: "X years ago" or "in X years"
9440 	 * </ul>
9441 	 * 
9442 	 * @param {IDate|number|string|Date|JulianDay|null|undefined} reference a date that the date parameter should be relative to
9443 	 * @param {IDate|number|string|Date|JulianDay|null|undefined} date a date being formatted
9444 	 * @throws "Wrong calendar type" when the start or end dates are not the same
9445 	 * calendar type as the formatter itself
9446 	 * @return {string} the formatted relative date
9447 	 */
9448 	formatRelative: function(reference, date) {
9449 		reference = DateFactory._dateToIlib(reference);
9450 		date = DateFactory._dateToIlib(date);
9451 
9452 		var referenceRd, dateRd, fmt, diff, absDiff, num;
9453 
9454 		if (typeof(reference) !== 'object' || !reference.getCalendar || reference.getCalendar() !== this.calName ||
9455 			typeof(date) !== 'object' || !date.getCalendar || date.getCalendar() !== this.calName) {
9456 			throw "Wrong calendar type";
9457 		}
9458 		
9459 		referenceRd = reference.getRataDie();
9460 		dateRd = date.getRataDie();
9461 
9462 		diff = referenceRd - dateRd;
9463 		absDiff = Math.abs(diff);
9464 
9465 		if (absDiff < 0.000694444) {
9466 			num = Math.round(absDiff * 86400);
9467 			switch (this.length) {
9468 				case 's':
9469 					fmt = diff > 0 ? this.sysres.getString("#{num}s ago") : this.sysres.getString("#in {num}s");
9470 					break;
9471 				case 'm':
9472 					fmt = diff > 0 ? this.sysres.getString("1#1 sec ago|#{num} sec ago") : this.sysres.getString("1#in 1 sec|#in {num} sec");
9473 					break;
9474 				default:
9475 				case 'f':
9476 				case 'l':
9477 					fmt = diff > 0 ? this.sysres.getString("1#1 second ago|#{num} seconds ago") : this.sysres.getString("1#in 1 second|#in {num} seconds");
9478 					break;
9479 			}
9480 		} else if (absDiff < 0.041666667) {
9481 			num = Math.round(absDiff * 1440);
9482 			switch (this.length) {
9483 				case 's':
9484 					fmt = diff > 0 ? this.sysres.getString("#{num}mi ago") : this.sysres.getString("#in {num}mi");
9485 					break;
9486 				case 'm':
9487 					fmt = diff > 0 ? this.sysres.getString("1#1 min ago|#{num} min ago") :  this.sysres.getString("1#in 1 min|#in {num} min");
9488 					break;
9489 				default:
9490 				case 'f':
9491 				case 'l':
9492 					fmt = diff > 0 ? this.sysres.getString("1#1 minute ago|#{num} minutes ago") : this.sysres.getString("1#in 1 minute|#in {num} minutes");
9493 					break;
9494 			}
9495 		} else if (absDiff < 1) {
9496 			num = Math.round(absDiff * 24);
9497 			switch (this.length) {
9498 				case 's':
9499 					fmt = diff > 0 ? this.sysres.getString("#{num}h ago") : this.sysres.getString("#in {num}h");
9500 					break;
9501 				case 'm':
9502 					fmt = diff > 0 ? this.sysres.getString("1#1 hr ago|#{num} hrs ago") : this.sysres.getString("1#in 1 hr|#in {num} hrs");
9503 					break;
9504 				default:
9505 				case 'f':
9506 				case 'l':
9507 					fmt = diff > 0 ? this.sysres.getString("1#1 hour ago|#{num} hours ago") : this.sysres.getString("1#in 1 hour|#in {num} hours");
9508 					break;
9509 			}
9510 		} else if (absDiff < 14) {
9511 			num = Math.round(absDiff);
9512 			switch (this.length) {
9513 				case 's':
9514 					fmt = diff > 0 ? this.sysres.getString("#{num}d ago") : this.sysres.getString("#in {num}d");
9515 					break;
9516 				case 'm':
9517 					fmt = diff > 0 ? this.sysres.getString("1#1 dy ago|#{nudurationm} dys ago") : this.sysres.getString("1#in 1 dy|#in {num} dys");
9518 					break;
9519 				default:
9520 				case 'f':
9521 				case 'l':
9522 					fmt = diff > 0 ? this.sysres.getString("1#1 day ago|#{num} days ago") : this.sysres.getString("1#in 1 day|#in {num} days");
9523 					break;
9524 			}
9525 		} else if (absDiff < 84) {
9526 			num = Math.round(absDiff/7);
9527 			switch (this.length) {
9528 				case 's':
9529 					fmt = diff > 0 ? this.sysres.getString("#{num}w ago") : this.sysres.getString("#in {num}w");
9530 					break;
9531 				case 'm':
9532 					fmt = diff > 0 ? this.sysres.getString("1#1 wk ago|#{num} wks ago") : this.sysres.getString("1#in 1 wk|#in {num} wks");
9533 					break;
9534 				default:
9535 				case 'f':
9536 				case 'l':
9537 					fmt = diff > 0 ? this.sysres.getString("1#1 week ago|#{num} weeks ago") : this.sysres.getString("1#in 1 week|#in {num} weeks");
9538 					break;
9539 			}
9540 		} else if (absDiff < 730) {
9541 			num = Math.round(absDiff/30.4);
9542 			switch (this.length) {
9543 				case 's':
9544 					fmt = diff > 0 ? this.sysres.getString("#{num}mo ago") : this.sysres.getString("#in {num}mo");
9545 					break;
9546 				case 'm':
9547 					fmt = diff > 0 ? this.sysres.getString("1#1 mon ago|#{num} mons ago") : this.sysres.getString("1#in 1 mon|#in {num} mons");
9548 					break;
9549 				default:
9550 				case 'f':
9551 				case 'l':
9552 					fmt = diff > 0 ? this.sysres.getString("1#1 month ago|#{num} months ago") : this.sysres.getString("1#in 1 month|#in {num} months");
9553 					break;
9554 			}
9555 		} else {
9556 			num = Math.round(absDiff/365);
9557 			switch (this.length) {
9558 				case 's':
9559 					fmt = diff > 0 ? this.sysres.getString("#{num}y ago") : this.sysres.getString("#in {num}y");
9560 					break;
9561 				case 'm':
9562 					fmt = diff > 0 ? this.sysres.getString("1#1 yr ago|#{num} yrs ago") : this.sysres.getString("1#in 1 yr|#in {num} yrs");
9563 					break;
9564 				default:
9565 				case 'f':
9566 				case 'l':
9567 					fmt = diff > 0 ? this.sysres.getString("1#1 year ago|#{num} years ago") : this.sysres.getString("1#in 1 year|#in {num} years");
9568 					break;
9569 			}
9570 		}
9571 		return fmt.formatChoice(num, {num: num});
9572 	}
9573 };
9574 
9575 
9576 
9577 /*< DateRngFmt.js */
9578 /*
9579  * DateRngFmt.js - Date formatter definition
9580  * 
9581  * Copyright © 2012-2015,2018, JEDLSoft
9582  *
9583  * Licensed under the Apache License, Version 2.0 (the "License");
9584  * you may not use this file except in compliance with the License.
9585  * You may obtain a copy of the License at
9586  *
9587  *     http://www.apache.org/licenses/LICENSE-2.0
9588  *
9589  * Unless required by applicable law or agreed to in writing, software
9590  * distributed under the License is distributed on an "AS IS" BASIS,
9591  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9592  *
9593  * See the License for the specific language governing permissions and
9594  * limitations under the License.
9595  */
9596 
9597 /*
9598 !depends 
9599 ilib.js 
9600 Locale.js 
9601 IString.js 
9602 CalendarFactory.js
9603 DateFmt.js
9604 GregorianCal.js
9605 JSUtils.js
9606 DateFactory.js
9607 */
9608 
9609 // !data dateformats sysres
9610 
9611 
9612 
9613 
9614 
9615 
9616 /**
9617  * @class
9618  * Create a new date range formatter instance. The date range formatter is immutable once
9619  * it is created, but can format as many different date ranges as needed with the same
9620  * options. Create different date range formatter instances for different purposes
9621  * and then keep them cached for use later if you have more than one range to
9622  * format.<p>
9623  * 
9624  * The options may contain any of the following properties:
9625  * 
9626  * <ul>
9627  * <li><i>locale</i> - locale to use when formatting the date/times in the range. If the 
9628  * locale is not specified, then the default locale of the app or web page will be used.
9629  * 
9630  * <li><i>calendar</i> - the type of calendar to use for this format. The value should
9631  * be a sting containing the name of the calendar. Currently, the supported
9632  * types are "gregorian", "julian", "arabic", "hebrew", or "chinese". If the
9633  * calendar is not specified, then the default calendar for the locale is used. When the
9634  * calendar type is specified, then the format method must be called with an instance of
9635  * the appropriate date type. (eg. Gregorian calendar means that the format method must 
9636  * be called with a GregDate instance.)
9637  *  
9638  * <li><i>timezone</i> - time zone to use when formatting times. This may be a time zone
9639  * instance or a time zone specifier string in RFC 822 format. If not specified, the
9640  * default time zone for the locale is used.
9641  * 
9642  * <li><i>length</i> - Specify the length of the format to use as a string. The length 
9643  * is the approximate size of the formatted string.
9644  * 
9645  * <ul>
9646  * <li><i>short</i> - use a short representation of the time. This is the most compact format possible for the locale.
9647  * <li><i>medium</i> - use a medium length representation of the time. This is a slightly longer format.
9648  * <li><i>long</i> - use a long representation of the time. This is a fully specified format, but some of the textual 
9649  * components may still be abbreviated. (eg. "Tue" instead of "Tuesday")
9650  * <li><i>full</i> - use a full representation of the time. This is a fully specified format where all the textual 
9651  * components are spelled out completely.
9652  * </ul>
9653  * 
9654  * eg. The "short" format for an en_US range may be "MM/yy - MM/yy", whereas the long format might be 
9655  * "MMM, yyyy - MMM, yyyy". In the long format, the month name is textual instead of numeric 
9656  * and is longer, the year is 4 digits instead of 2, and the format contains slightly more 
9657  * spaces and formatting characters.<p>
9658  * 
9659  * Note that the length parameter does not specify which components are to be formatted. The
9660  * components that are formatted depend on the length of time in the range.
9661  * 
9662  * <li><i>clock</i> - specify that formatted times should use a 12 or 24 hour clock if the
9663  * format happens to include times. Valid values are "12" and "24".<p>
9664  * 
9665  * In some locales, both clocks are used. For example, in en_US, the general populace uses
9666  * a 12 hour clock with am/pm, but in the US military or in nautical or aeronautical or 
9667  * scientific writing, it is more common to use a 24 hour clock. This property allows you to
9668  * construct a formatter that overrides the default for the locale.<p>
9669  * 
9670  * If this property is not specified, the default is to use the most widely used convention
9671  * for the locale.
9672  * <li>onLoad - a callback function to call when the date range format object is fully 
9673  * loaded. When the onLoad option is given, the DateRngFmt object will attempt to
9674  * load any missing locale data using the ilib loader callback.
9675  * When the constructor is done (even if the data is already preassembled), the 
9676  * onLoad function is called with the current instance as a parameter, so this
9677  * callback can be used with preassembled or dynamic loading or a mix of the two. 
9678  * 
9679  * <li>sync - tell whether to load any missing locale data synchronously or 
9680  * asynchronously. If this option is given as "false", then the "onLoad"
9681  * callback must be given, as the instance returned from this constructor will
9682  * not be usable for a while.
9683  *  
9684  * <li><i>loadParams</i> - an object containing parameters to pass to the 
9685  * loader callback function when locale data is missing. The parameters are not
9686  * interpretted or modified in any way. They are simply passed along. The object 
9687  * may contain any property/value pairs as long as the calling code is in
9688  * agreement with the loader callback function as to what those parameters mean.
9689  * </ul>
9690  * <p>
9691  * 
9692  * 
9693  * @constructor
9694  * @param {Object} options options governing the way this date range formatter instance works
9695  */
9696 var DateRngFmt = function(options) {
9697 	var sync = true;
9698 	var loadParams = undefined;
9699 	this.locale = new Locale();
9700 	this.length = "s";
9701 	
9702 	if (options) {
9703 		if (options.locale) {
9704 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
9705 		}
9706 		
9707 		if (options.calendar) {
9708 			this.calName = options.calendar;
9709 		}
9710 		
9711 		if (options.length) {
9712 			if (options.length === 'short' ||
9713 				options.length === 'medium' ||
9714 				options.length === 'long' ||
9715 				options.length === 'full') {
9716 				// only use the first char to save space in the json files
9717 				this.length = options.length.charAt(0);
9718 			}
9719 		}
9720 		if (typeof(options.sync) !== 'undefined') {
9721 			sync = !!options.sync;
9722 		}
9723 		
9724 		loadParams = options.loadParams;
9725 	}
9726 	
9727 	var opts = {};
9728 	JSUtils.shallowCopy(options, opts);
9729 	opts.sync = sync;
9730 	opts.loadParams = loadParams;
9731 	
9732 	/**
9733 	 * @private
9734 	 */
9735 	opts.onLoad = ilib.bind(this, function (fmt) {
9736 		this.dateFmt = fmt;
9737 		if (fmt) {
9738 			this.locinfo = this.dateFmt.locinfo;
9739 
9740 			// get the default calendar name from the locale, and if the locale doesn't define
9741 			// one, use the hard-coded gregorian as the last resort
9742 			this.calName = this.calName || this.locinfo.getCalendar() || "gregorian";
9743 			CalendarFactory({
9744 				type: this.calName,
9745 				sync: sync,
9746 				loadParams: loadParams,
9747 				onLoad: ilib.bind(this, function(cal) {
9748 				    this.cal = cal;
9749 				    
9750 				    if (!this.cal) {
9751 				        // always synchronous
9752 				        this.cal = new GregorianCal();
9753 				    }
9754 				    
9755 				    this.timeTemplate = this.dateFmt._getFormat(this.dateFmt.formats.time[this.dateFmt.clock], this.dateFmt.timeComponents, this.length) || "hh:mm";
9756 				    this.timeTemplateArr = this.dateFmt._tokenize(this.timeTemplate);
9757 				    
9758 				    if (options && typeof(options.onLoad) === 'function') {
9759 				        options.onLoad(this);
9760 				    }
9761 				})
9762 			});
9763 		} else {
9764 		    if (options && typeof(options.sync) === "boolean" && !options.sync && typeof(options.onLoad) === 'function') {
9765                 options.onLoad(undefined);
9766             } else {
9767                 throw "No formats available for calendar " + this.calName + " in locale " + this.locale.getSpec();
9768             }
9769 		}
9770 	});
9771 
9772 	// delegate a bunch of the formatting to this formatter
9773 	new DateFmt(opts);
9774 };
9775 
9776 DateRngFmt.prototype = {
9777 	/**
9778 	 * Return the locale used with this formatter instance.
9779 	 * @return {Locale} the Locale instance for this formatter
9780 	 */
9781 	getLocale: function() {
9782 		return this.locale;
9783 	},
9784 	
9785 	/**
9786 	 * Return the name of the calendar used to format date/times for this
9787 	 * formatter instance.
9788 	 * @return {string} the name of the calendar used by this formatter
9789 	 */
9790 	getCalendar: function () {
9791 		return this.dateFmt.getCalendar();
9792 	},
9793 	
9794 	/**
9795 	 * Return the length used to format date/times in this formatter. This is either the
9796 	 * value of the length option to the constructor, or the default value.
9797 	 * 
9798 	 * @return {string} the length of formats this formatter returns
9799 	 */
9800 	getLength: function () {
9801 		return DateFmt.lenmap[this.length] || "";
9802 	},
9803 	
9804 	/**
9805 	 * Return the time zone used to format date/times for this formatter
9806 	 * instance.
9807 	 * @return {TimeZone} a string naming the time zone
9808 	 */
9809 	getTimeZone: function () {
9810 		return this.dateFmt.getTimeZone();
9811 	},
9812 	
9813 	/**
9814 	 * Return the clock option set in the constructor. If the clock option was
9815 	 * not given, the default from the locale is returned instead.
9816 	 * @return {string} "12" or "24" depending on whether this formatter uses
9817 	 * the 12-hour or 24-hour clock
9818 	 */
9819 	getClock: function () {
9820 		return this.dateFmt.getClock();
9821 	},
9822 	
9823 	/**
9824 	 * Format a date/time range according to the settings of the current
9825 	 * formatter. The range is specified as being from the "start" date until
9826 	 * the "end" date. <p>
9827 	 * 
9828 	 * The template that the date/time range uses depends on the
9829 	 * length of time between the dates, on the premise that a long date range
9830 	 * which is too specific is not useful. For example, when giving
9831 	 * the dates of the 100 Years War, in most situations it would be more 
9832 	 * appropriate to format the range as "1337 - 1453" than to format it as 
9833 	 * "10:37am November 9, 1337 - 4:37pm July 17, 1453", as the latter format 
9834 	 * is much too specific given the length of time that the range represents.
9835 	 * If a very specific, but long, date range really is needed, the caller 
9836 	 * should format two specific dates separately and put them 
9837 	 * together as you might with other normal strings.<p>
9838 	 * 
9839 	 * The format used for a date range contains the following date components,
9840 	 * where the order of those components is rearranged and the component values 
9841 	 * are translated according to each locale:
9842 	 * 
9843 	 * <ul>
9844 	 * <li>within 3 days: the times of day, dates, months, and years
9845 	 * <li>within 730 days (2 years): the dates, months, and years
9846 	 * <li>within 3650 days (10 years): the months and years
9847 	 * <li>longer than 10 years: the years only 
9848 	 * </ul>
9849 	 * 
9850 	 * In general, if any of the date components share a value between the
9851 	 * start and end date, that component is only given once. For example,
9852 	 * if the range is from November 15, 2011 to November 26, 2011, the 
9853 	 * start and end dates both share the same month and year. The 
9854 	 * range would then be formatted as "November 15-26, 2011". <p>
9855 	 * 
9856 	 * If you want to format a length of time instead of a particular range of
9857 	 * time (for example, the length of an event rather than the specific start time
9858 	 * and end time of that event), then use a duration formatter instance 
9859 	 * (DurationFmt) instead. The formatRange method will make sure that each component 
9860 	 * of the date/time is within the normal range for that component. For example, 
9861 	 * the minutes will always be between 0 and 59, no matter what is specified in 
9862 	 * the date to format, because that is the normal range for minutes. A duration 
9863 	 * format will allow the number of minutes to exceed 59. For example, if you 
9864 	 * were displaying the length of a movie that is 198 minutes long, the minutes
9865 	 * component of a duration could be 198.<p>
9866 	 * 
9867 	 * @param {IDate|Date|number|string} startDateLike the starting date/time of the range. The
9868 	 * date may be given as an ilib IDate object, a javascript intrinsic Date object, a
9869 	 * unix time, or a date string parsable by the javscript Date.
9870 	 * @param {IDate|Date|number|string} endDateLike the ending date/time of the range. The
9871 	 * date may be given as an ilib IDate object, a javascript intrinsic Date object, a
9872 	 * unix time, or a date string parsable by the javscript Date.
9873 	 * @throws "Wrong calendar type" when the start or end dates are not the same
9874 	 * calendar type as the formatter itself
9875 	 * @return {string} a date range formatted for the locale
9876 	 */
9877 	format: function (startDateLike, endDateLike) {
9878 		var startRd, endRd, fmt = "", yearTemplate, monthTemplate, dayTemplate, formats;
9879 		var thisZoneName = this.dateFmt.tz && this.dateFmt.tz.getId() || "local";
9880 
9881 		var start = DateFactory._dateToIlib(startDateLike, thisZoneName, this.locale);
9882 		var end = DateFactory._dateToIlib(endDateLike, thisZoneName, this.locale);
9883 		
9884 		if (typeof(start) !== 'object' || !start.getCalendar || start.getCalendar() !== this.calName ||
9885 			typeof(end) !== 'object' || !end.getCalendar || end.getCalendar() !== this.calName) {
9886 			throw "Wrong calendar type";
9887 		}
9888 		
9889 		startRd = start.getRataDie();
9890 		endRd = end.getRataDie();
9891 		
9892 		// 
9893 		// legend:
9894 		// c00 - difference is less than 3 days. Year, month, and date are same, but time is different
9895 		// c01 - difference is less than 3 days. Year and month are same but date and time are different
9896 		// c02 - difference is less than 3 days. Year is same but month, date, and time are different. (ie. it straddles a month boundary)
9897 		// c03 - difference is less than 3 days. Year, month, date, and time are all different. (ie. it straddles a year boundary)
9898 		// c10 - difference is less than 2 years. Year and month are the same, but date is different.
9899 		// c11 - difference is less than 2 years. Year is the same, but month, date, and time are different.
9900 		// c12 - difference is less than 2 years. All fields are different. (ie. straddles a year boundary)
9901 		// c20 - difference is less than 10 years. All fields are different.
9902 		// c30 - difference is more than 10 years. All fields are different.
9903 		//
9904 		
9905 		if (endRd - startRd < 3) {
9906 			if (start.year === end.year) {
9907 				if (start.month === end.month) {
9908 					if (start.day === end.day) {
9909 						fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c00", this.length));
9910 					} else {
9911 						fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c01", this.length));
9912 					}
9913 				} else {
9914 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c02", this.length));
9915 				}
9916 			} else {
9917 				fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c03", this.length));
9918 			}
9919 		} else if (endRd - startRd < 730) {
9920 			if (start.year === end.year) {
9921 				if (start.month === end.month) {
9922 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c10", this.length));
9923 				} else {
9924 					fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c11", this.length));
9925 				}
9926 			} else {
9927 				fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c12", this.length));
9928 			}
9929 		} else if (endRd - startRd < 3650) {
9930 			fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c20", this.length));
9931 		} else {
9932 			fmt = new IString(this.dateFmt._getFormat(this.dateFmt.formats.range, "c30", this.length));
9933 		}
9934 
9935 		formats = this.dateFmt.formats.date;
9936 		yearTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "y", this.length) || "yyyy");
9937 		monthTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "m", this.length) || "MM");
9938 		dayTemplate = this.dateFmt._tokenize(this.dateFmt._getFormatInternal(formats, "d", this.length) || "dd");
9939 		
9940 		/*
9941 		console.log("fmt is " + fmt.toString());
9942 		console.log("year template is " + yearTemplate);
9943 		console.log("month template is " + monthTemplate);
9944 		console.log("day template is " + dayTemplate);
9945 		*/
9946 		
9947 		return fmt.format({
9948 			sy: this.dateFmt._formatTemplate(start, yearTemplate),
9949 			sm: this.dateFmt._formatTemplate(start, monthTemplate),
9950 			sd: this.dateFmt._formatTemplate(start, dayTemplate),
9951 			st: this.dateFmt._formatTemplate(start, this.timeTemplateArr),
9952 			ey: this.dateFmt._formatTemplate(end, yearTemplate),
9953 			em: this.dateFmt._formatTemplate(end, monthTemplate),
9954 			ed: this.dateFmt._formatTemplate(end, dayTemplate),
9955 			et: this.dateFmt._formatTemplate(end, this.timeTemplateArr)
9956 		});
9957 	}
9958 };
9959 
9960 
9961 /*< HebrewCal.js */
9962 /*
9963  * hebrew.js - Represent a Hebrew calendar object.
9964  * 
9965  * Copyright © 2012-2015,2018, JEDLSoft
9966  *
9967  * Licensed under the Apache License, Version 2.0 (the "License");
9968  * you may not use this file except in compliance with the License.
9969  * You may obtain a copy of the License at
9970  *
9971  *     http://www.apache.org/licenses/LICENSE-2.0
9972  *
9973  * Unless required by applicable law or agreed to in writing, software
9974  * distributed under the License is distributed on an "AS IS" BASIS,
9975  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9976  *
9977  * See the License for the specific language governing permissions and
9978  * limitations under the License.
9979  */
9980 
9981 
9982 /* !depends Calendar.js MathUtils.js */
9983 
9984 
9985 /**
9986  * @class
9987  * Construct a new Hebrew calendar object. This class encodes information about
9988  * the Hebrew (Jewish) calendar. The Hebrew calendar is a tabular hebrew 
9989  * calendar where the dates are calculated by arithmetic rules. This differs from 
9990  * the religious Hebrew calendar which is used to mark the beginning of particular 
9991  * holidays. The religious calendar depends on the first sighting of the new 
9992  * crescent moon to determine the first day of the new month. Because humans and 
9993  * weather are both involved, the actual time of sighting varies, so it is not 
9994  * really possible to precalculate the religious calendar. Certain groups, such 
9995  * as the Hebrew Society of North America, decreed in in 2007 that they will use
9996  * a calendar based on calculations rather than observations to determine the 
9997  * beginning of lunar months, and therefore the dates of holidays.<p>
9998  * 
9999  * @param {Object=} options Options governing the construction of this instance
10000  * @constructor
10001  * @extends Calendar
10002  */
10003 var HebrewCal = function(options) {
10004 	this.type = "hebrew";
10005     
10006     if (options && typeof(options.onLoad) === "function") {
10007         options.onLoad(this);
10008     }
10009 };
10010 
10011 /**
10012  * Return the number of days elapsed in the Hebrew calendar before the
10013  * given year starts.
10014  * @private
10015  * @param {number} year the year for which the number of days is sought
10016  * @return {number} the number of days elapsed in the Hebrew calendar before the
10017  * given year starts
10018  */
10019 HebrewCal.elapsedDays = function(year) {
10020 	var months = Math.floor(((235*year) - 234)/19);
10021 	var parts = 204 + 793 * MathUtils.mod(months, 1080);
10022 	var hours = 11 + 12 * months + 793 * Math.floor(months/1080) + 
10023 		Math.floor(parts/1080);
10024 	var days = 29 * months + Math.floor(hours/24);
10025 	return (MathUtils.mod(3 * (days + 1), 7) < 3) ? days + 1 : days;
10026 };
10027 
10028 /**
10029  * Return the number of days that the New Year's (Rosh HaShanah) in the Hebrew 
10030  * calendar will be corrected for the given year. Corrections are caused because New 
10031  * Year's is not allowed to start on certain days of the week. To deal with 
10032  * it, the start of the new year is corrected for the next year by adding a 
10033  * day to the 8th month (Heshvan) and/or the 9th month (Kislev) in the current
10034  * year to make them 30 days long instead of 29.
10035  * 
10036  * @private
10037  * @param {number} year the year for which the correction is sought
10038  * @param {number} elapsed number of days elapsed up to this year
10039  * @return {number} the number of days correction in the current year to make sure
10040  * Rosh HaShanah does not fall on undesirable days of the week
10041  */
10042 HebrewCal.newYearsCorrection = function(year, elapsed) {
10043 	var lastYear = HebrewCal.elapsedDays(year-1),
10044 		thisYear = elapsed,
10045 		nextYear = HebrewCal.elapsedDays(year+1);
10046 	
10047 	return (nextYear - thisYear) == 356 ? 2 : ((thisYear - lastYear) == 382 ? 1 : 0);
10048 };
10049 
10050 /**
10051  * Return the rata die date of the new year for the given hebrew year.
10052  * @private
10053  * @param {number} year the year for which the new year is needed
10054  * @return {number} the rata die date of the new year
10055  */
10056 HebrewCal.newYear = function(year) {
10057 	var elapsed = HebrewCal.elapsedDays(year); 
10058 	
10059 	return elapsed + HebrewCal.newYearsCorrection(year, elapsed);
10060 };
10061 
10062 /**
10063  * Return the number of days in the given year. Years contain a variable number of
10064  * days because the date of Rosh HaShanah (New Year's) changes so that it doesn't
10065  * fall on particular days of the week. Days are added to the months of Heshvan
10066  * and/or Kislev in the previous year in order to prevent the current year's New
10067  * Year from being on Sunday, Wednesday, or Friday.
10068  * 
10069  * @param {number} year the year for which the length is sought
10070  * @return {number} number of days in the given year
10071  */
10072 HebrewCal.daysInYear = function(year) {
10073 	return HebrewCal.newYear(year+1) - HebrewCal.newYear(year);
10074 };
10075 
10076 /**
10077  * Return true if the given year contains a long month of Heshvan. That is,
10078  * it is 30 days instead of 29.
10079  * 
10080  * @private
10081  * @param {number} year the year in which that month is questioned
10082  * @return {boolean} true if the given year contains a long month of Heshvan
10083  */
10084 HebrewCal.longHeshvan = function(year) {
10085 	return MathUtils.mod(HebrewCal.daysInYear(year), 10) === 5;
10086 };
10087 
10088 /**
10089  * Return true if the given year contains a long month of Kislev. That is,
10090  * it is 30 days instead of 29.
10091  * 
10092  * @private
10093  * @param {number} year the year in which that month is questioned
10094  * @return {boolean} true if the given year contains a short month of Kislev
10095  */
10096 HebrewCal.longKislev = function(year) {
10097 	return MathUtils.mod(HebrewCal.daysInYear(year), 10) !== 3;
10098 };
10099 
10100 /**
10101  * Return the date of the last day of the month for the given year. The date of
10102  * the last day of the month is variable because a number of months gain an extra 
10103  * day in leap years, and it is variable which months gain a day for each leap 
10104  * year and which do not.
10105  * 
10106  * @param {number} month the month for which the number of days is sought
10107  * @param {number} year the year in which that month is
10108  * @return {number} the number of days in the given month and year
10109  */
10110 HebrewCal.prototype.lastDayOfMonth = function(month, year) {
10111 	switch (month) {
10112 		case 2: 
10113 		case 4: 
10114 		case 6: 
10115 		case 10: 
10116 			return 29;
10117 		case 13:
10118 			return this.isLeapYear(year) ? 29 : 0;
10119 		case 8:
10120 			return HebrewCal.longHeshvan(year) ? 30 : 29;
10121 		case 9:
10122 			return HebrewCal.longKislev(year) ? 30 : 29;
10123 		case 12:
10124 		case 1:
10125 		case 3:
10126 		case 5:
10127 		case 7:
10128 		case 11:
10129 			return 30;
10130 		default:
10131 			return 0;
10132 	}
10133 };
10134 
10135 /**
10136  * Return the number of months in the given year. The number of months in a year varies
10137  * for luni-solar calendars because in some years, an extra month is needed to extend the 
10138  * days in a year to an entire solar year. The month is represented as a 1-based number
10139  * where 1=first month, 2=second month, etc.
10140  * 
10141  * @param {number} year a year for which the number of months is sought
10142  */
10143 HebrewCal.prototype.getNumMonths = function(year) {
10144 	return this.isLeapYear(year) ? 13 : 12;
10145 };
10146 
10147 /**
10148  * Return the number of days in a particular month in a particular year. This function
10149  * can return a different number for a month depending on the year because of leap years.
10150  *
10151  * @param {number} month the month for which the length is sought
10152  * @param {number} year the year within which that month can be found
10153  * @returns {number} the number of days within the given month in the given year, or
10154  * 0 for an invalid month in the year
10155  */
10156 HebrewCal.prototype.getMonLength = function(month, year) {
10157 	if (month < 1 || month > 13 || (month == 13 && !this.isLeapYear(year))) {
10158 		return 0;
10159 	}
10160 	return this.lastDayOfMonth(month, year);
10161 };
10162 
10163 /**
10164  * Return true if the given year is a leap year in the Hebrew calendar.
10165  * The year parameter may be given as a number, or as a HebrewDate object.
10166  * @param {number|Object} year the year for which the leap year information is being sought
10167  * @returns {boolean} true if the given year is a leap year
10168  */
10169 HebrewCal.prototype.isLeapYear = function(year) {
10170 	var y = (typeof(year) == 'number') ? year : year.year;
10171 	return (MathUtils.mod(1 + 7 * y, 19) < 7);
10172 };
10173 
10174 /**
10175  * Return the type of this calendar.
10176  * 
10177  * @returns {string} the name of the type of this calendar 
10178  */
10179 HebrewCal.prototype.getType = function() {
10180 	return this.type;
10181 };
10182 
10183 
10184 /*register this calendar for the factory method */
10185 Calendar._constructors["hebrew"] = HebrewCal;
10186 
10187 
10188 
10189 /*< HebrewRataDie.js */
10190 /*
10191  * HebrewRataDie.js - Represent an RD date in the Hebrew calendar
10192  * 
10193  * Copyright © 2012-2015, JEDLSoft
10194  *
10195  * Licensed under the Apache License, Version 2.0 (the "License");
10196  * you may not use this file except in compliance with the License.
10197  * You may obtain a copy of the License at
10198  *
10199  *     http://www.apache.org/licenses/LICENSE-2.0
10200  *
10201  * Unless required by applicable law or agreed to in writing, software
10202  * distributed under the License is distributed on an "AS IS" BASIS,
10203  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10204  *
10205  * See the License for the specific language governing permissions and
10206  * limitations under the License.
10207  */
10208 
10209 /* !depends 
10210 MathUtils.js
10211 HebrewCal.js
10212 RataDie.js
10213 */
10214 
10215 
10216 /**
10217  * @class
10218  * Construct a new Hebrew RD date number object. The constructor parameters can 
10219  * contain any of the following properties:
10220  * 
10221  * <ul>
10222  * <li><i>unixtime<i> - sets the time of this instance according to the given 
10223  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
10224  * 
10225  * <li><i>julianday</i> - sets the time of this instance according to the given
10226  * Julian Day instance or the Julian Day given as a float
10227  * 
10228  * <li><i>year</i> - any integer, including 0
10229  * 
10230  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
10231  * 
10232  * <li><i>day</i> - 1 to 31
10233  * 
10234  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
10235  * is always done with an unambiguous 24 hour representation
10236  * 
10237  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify 
10238  * the parts or specify the minutes, seconds, and milliseconds, but not both. 
10239  * 
10240  * <li><i>minute</i> - 0 to 59
10241  * 
10242  * <li><i>second</i> - 0 to 59
10243  * 
10244  * <li><i>millisecond</i> - 0 to 999
10245  * 
10246  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
10247  * </ul>
10248  *
10249  * If the constructor is called with another Hebrew date instance instead of
10250  * a parameter block, the other instance acts as a parameter block and its
10251  * settings are copied into the current instance.<p>
10252  * 
10253  * If the constructor is called with no arguments at all or if none of the 
10254  * properties listed above are present, then the RD is calculate based on 
10255  * the current date at the time of instantiation. <p>
10256  * 
10257  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
10258  * specified in the params, it is assumed that they have the smallest possible
10259  * value in the range for the property (zero or one).<p>
10260  * 
10261  * 
10262  * @private
10263  * @constructor
10264  * @extends RataDie
10265  * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew RD date
10266  */
10267 var HebrewRataDie = function(params) {
10268 	this.cal = params && params.cal || new HebrewCal();
10269 	this.rd = NaN;
10270 	RataDie.call(this, params);
10271 };
10272 
10273 HebrewRataDie.prototype = new RataDie();
10274 HebrewRataDie.prototype.parent = RataDie;
10275 HebrewRataDie.prototype.constructor = HebrewRataDie;
10276 
10277 /**
10278  * The difference between a zero Julian day and the first day of the Hebrew 
10279  * calendar: sunset on Monday, Tishri 1, 1 = September 7, 3760 BC Gregorian = JD 347997.25
10280  * @private
10281  * @type number
10282  */
10283 HebrewRataDie.prototype.epoch = 347997.25;
10284 
10285 /**
10286  * the cumulative lengths of each month for a non-leap year, without new years corrections
10287  * @private
10288  * @const
10289  * @type Array.<number>
10290  */
10291 HebrewRataDie.cumMonthLengths = [
10292 	176,  /* Nisan */
10293 	206,  /* Iyyar */
10294 	235,  /* Sivan */
10295 	265,  /* Tammuz */
10296 	294,  /* Av */
10297 	324,  /* Elul */
10298 	0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10299 	30,   /* Heshvan */
10300 	59,   /* Kislev */
10301 	88,   /* Teveth */
10302 	117,  /* Shevat */
10303 	147   /* Adar I */
10304 ];
10305 
10306 /**
10307  * the cumulative lengths of each month for a leap year, without new years corrections 
10308  * @private
10309  * @const
10310  * @type Array.<number>
10311  */
10312 HebrewRataDie.cumMonthLengthsLeap = [
10313 	206,  /* Nisan */
10314 	236,  /* Iyyar */
10315 	265,  /* Sivan */
10316 	295,  /* Tammuz */
10317 	324,  /* Av */
10318 	354,  /* Elul */
10319 	0,    /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10320 	30,   /* Heshvan */
10321 	59,   /* Kislev */
10322 	88,   /* Teveth */
10323 	117,  /* Shevat */
10324 	147,  /* Adar I */
10325 	177   /* Adar II */
10326 ];
10327 
10328 /**
10329  * Calculate the Rata Die (fixed day) number of the given date from the
10330  * date components.
10331  * 
10332  * @private
10333  * @param {Object} date the date components to calculate the RD from
10334  */
10335 HebrewRataDie.prototype._setDateComponents = function(date) {
10336 	var elapsed = HebrewCal.elapsedDays(date.year);
10337 	var days = elapsed +
10338 		HebrewCal.newYearsCorrection(date.year, elapsed) +
10339 		date.day - 1;
10340 	var sum = 0, table;
10341 	
10342 	//console.log("getRataDie: converting " +  JSON.stringify(date));
10343 	//console.log("getRataDie: days is " +  days);
10344 	//console.log("getRataDie: new years correction is " +  HebrewCal.newYearsCorrection(date.year, elapsed));
10345 	
10346 	table = this.cal.isLeapYear(date.year) ? 
10347 		HebrewRataDie.cumMonthLengthsLeap :
10348 		HebrewRataDie.cumMonthLengths;
10349 	sum = table[date.month-1];
10350 	
10351 	// gets cumulative without correction, so now add in the correction
10352 	if ((date.month < 7 || date.month > 8) && HebrewCal.longHeshvan(date.year)) {
10353 		sum++;
10354 	}
10355 	if ((date.month < 7 || date.month > 9) && HebrewCal.longKislev(date.year)) {
10356 		sum++;
10357 	}
10358 	// console.log("getRataDie: cum days is now " +  sum);
10359 	
10360 	days += sum;
10361 	
10362 	// the date starts at sunset, which we take as 18:00, so the hours from
10363 	// midnight to 18:00 are on the current Gregorian day, and the hours from
10364 	// 18:00 to midnight are on the previous Gregorian day. So to calculate the 
10365 	// number of hours into the current day that this time represents, we have
10366 	// to count from 18:00 to midnight first, and add in 6 hours if the time is
10367 	// less than 18:00
10368 	var minute, second, millisecond;
10369 	
10370 	if (typeof(date.parts) !== 'undefined') {
10371 		// The parts (halaqim) of the hour. This can be a number from 0 to 1079.
10372 		var parts = parseInt(date.parts, 10);
10373 		var seconds = parseInt(parts, 10) * 3.333333333333;
10374 		minute = Math.floor(seconds / 60);
10375 		seconds -= minute * 60;
10376 		second = Math.floor(seconds);
10377 		millisecond = (seconds - second);	
10378 	} else {
10379 		minute = parseInt(date.minute, 10) || 0;
10380 		second = parseInt(date.second, 10) || 0;
10381 		millisecond = parseInt(date.millisecond, 10) || 0;
10382 	}
10383 		
10384 	var time;
10385 	if (date.hour >= 18) {
10386 		time = ((date.hour - 18 || 0) * 3600000 +
10387 			(minute || 0) * 60000 +
10388 			(second || 0) * 1000 +
10389 			(millisecond || 0)) / 
10390 			86400000;
10391 	} else {
10392 		time = 0.25 +	// 6 hours from 18:00 to midnight on the previous gregorian day
10393 				((date.hour || 0) * 3600000 +
10394 				(minute || 0) * 60000 +
10395 				(second || 0) * 1000 +
10396 				(millisecond || 0)) / 
10397 				86400000;
10398 	}
10399 	
10400 	//console.log("getRataDie: rd is " +  (days + time));
10401 	this.rd = days + time;
10402 };
10403 	
10404 /**
10405  * Return the rd number of the particular day of the week on or before the 
10406  * given rd. eg. The Sunday on or before the given rd.
10407  * @private
10408  * @param {number} rd the rata die date of the reference date
10409  * @param {number} dayOfWeek the day of the week that is being sought relative 
10410  * to the current date
10411  * @return {number} the rd of the day of the week
10412  */
10413 HebrewRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
10414 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek + 1, 7);
10415 };
10416 
10417 
10418 
10419 /*< HebrewDate.js */
10420 /*
10421  * HebrewDate.js - Represent a date in the Hebrew calendar
10422  *
10423  * Copyright © 2012-2015, 2018, JEDLSoft
10424  *
10425  * Licensed under the Apache License, Version 2.0 (the "License");
10426  * you may not use this file except in compliance with the License.
10427  * You may obtain a copy of the License at
10428  *
10429  *     http://www.apache.org/licenses/LICENSE-2.0
10430  *
10431  * Unless required by applicable law or agreed to in writing, software
10432  * distributed under the License is distributed on an "AS IS" BASIS,
10433  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10434  *
10435  * See the License for the specific language governing permissions and
10436  * limitations under the License.
10437  */
10438 
10439 /* !depends
10440 ilib.js
10441 Locale.js
10442 LocaleInfo.js
10443 TimeZone.js
10444 IDate.js
10445 MathUtils.js
10446 HebrewCal.js
10447 HebrewRataDie.js
10448 */
10449 
10450 
10451 
10452 
10453 /**
10454  * @class
10455  * Construct a new civil Hebrew date object. The constructor can be called
10456  * with a params object that can contain the following properties:<p>
10457  *
10458  * <ul>
10459  * <li><i>julianday</i> - the Julian Day to set into this date
10460  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year
10461  * <li><i>month</i> - 1 to 12, where 1 means Nisan, 2 means Iyyar, etc.
10462  * <li><i>day</i> - 1 to 30
10463  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
10464  * is always done with an unambiguous 24 hour representation
10465  * <li><i>parts</i> - 0 to 1079. Specify the halaqim parts of an hour. Either specify
10466  * the parts or specify the minutes, seconds, and milliseconds, but not both.
10467  * <li><i>minute</i> - 0 to 59
10468  * <li><i>second</i> - 0 to 59
10469  * <li><i>millisecond</i> - 0 to 999
10470  * <li><i>locale</i> - the TimeZone instance or time zone name as a string
10471  * of this julian date. The date/time is kept in the local time. The time zone
10472  * is used later if this date is formatted according to a different time zone and
10473  * the difference has to be calculated, or when the date format has a time zone
10474  * component in it.
10475  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not
10476  * given, it can be inferred from this locale. For locales that span multiple
10477  * time zones, the one with the largest population is chosen as the one that
10478  * represents the locale.
10479  *
10480  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
10481  * </ul>
10482  *
10483  * If called with another Hebrew date argument, the date components of the given
10484  * date are copied into the current one.<p>
10485  *
10486  * If the constructor is called with no arguments at all or if none of the
10487  * properties listed above
10488  * from <i>julianday</i> through <i>millisecond</i> are present, then the date
10489  * components are
10490  * filled in with the current date at the time of instantiation. Note that if
10491  * you do not give the time zone when defaulting to the current time and the
10492  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
10493  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich
10494  * Mean Time").<p>
10495  *
10496  *
10497  * @constructor
10498  * @extends IDate
10499  * @param {Object=} params parameters that govern the settings and behaviour of this Hebrew date
10500  */
10501 var HebrewDate = function(params) {
10502     this.cal = new HebrewCal();
10503 
10504     params = params || {};
10505 
10506     if (params.timezone) {
10507         this.timezone = params.timezone;
10508     }
10509     if (params.locale) {
10510         this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
10511     }
10512 
10513     if (!this.timezone) {
10514         if (this.locale) {
10515             new LocaleInfo(this.locale, {
10516                 sync: params.sync,
10517                 loadParams: params.loadParams,
10518                 onLoad: ilib.bind(this, function(li) {
10519                     this.li = li;
10520                     this.timezone = li.getTimeZone();
10521                     this._init(params);
10522                 })
10523             });
10524         } else {
10525             this.timezone = "local";
10526             this._init(params);
10527         }
10528     } else {
10529         this._init(params);
10530     }
10531 };
10532 
10533 HebrewDate.prototype = new IDate({noinstance: true});
10534 HebrewDate.prototype.parent = IDate;
10535 HebrewDate.prototype.constructor = HebrewDate;
10536 
10537 /**
10538  * Initialize this date
10539  * @private
10540  */
10541 HebrewDate.prototype._init = function (params) {
10542     if (params.year || params.month || params.day || params.hour ||
10543         params.minute || params.second || params.millisecond || params.parts ) {
10544         /**
10545          * Year in the Hebrew calendar.
10546          * @type number
10547          */
10548         this.year = parseInt(params.year, 10) || 0;
10549 
10550         /**
10551          * The month number, ranging from 1 to 13.
10552          * @type number
10553          */
10554         this.month = parseInt(params.month, 10) || 1;
10555 
10556         /**
10557          * The day of the month. This ranges from 1 to 30.
10558          * @type number
10559          */
10560         this.day = parseInt(params.day, 10) || 1;
10561 
10562         /**
10563          * The hour of the day. This can be a number from 0 to 23, as times are
10564          * stored unambiguously in the 24-hour clock.
10565          * @type number
10566          */
10567         this.hour = parseInt(params.hour, 10) || 0;
10568 
10569         if (typeof(params.parts) !== 'undefined') {
10570             /**
10571              * The parts (halaqim) of the hour. This can be a number from 0 to 1079.
10572              * @type number
10573              */
10574             this.parts = parseInt(params.parts, 10);
10575             var seconds = parseInt(params.parts, 10) * 3.333333333333;
10576             this.minute = Math.floor(seconds / 60);
10577             seconds -= this.minute * 60;
10578             this.second = Math.floor(seconds);
10579             this.millisecond = (seconds - this.second);
10580         } else {
10581             /**
10582              * The minute of the hours. Ranges from 0 to 59.
10583              * @type number
10584              */
10585             this.minute = parseInt(params.minute, 10) || 0;
10586 
10587             /**
10588              * The second of the minute. Ranges from 0 to 59.
10589              * @type number
10590              */
10591             this.second = parseInt(params.second, 10) || 0;
10592 
10593             /**
10594              * The millisecond of the second. Ranges from 0 to 999.
10595              * @type number
10596              */
10597             this.millisecond = parseInt(params.millisecond, 10) || 0;
10598         }
10599 
10600         /**
10601          * The day of the year. Ranges from 1 to 383.
10602          * @type number
10603          */
10604         this.dayOfYear = parseInt(params.dayOfYear, 10);
10605 
10606         if (typeof(params.dst) === 'boolean') {
10607             this.dst = params.dst;
10608         }
10609 
10610         this.rd = this.newRd(this);
10611 
10612         // add the time zone offset to the rd to convert to UTC
10613         new TimeZone({
10614             id: this.timezone,
10615             sync: params.sync,
10616             loadParams: params.loadParams,
10617             onLoad: ilib.bind(this, function(tz) {
10618                 this.tz = tz;
10619                 // getOffsetMillis requires that this.year, this.rd, and this.dst
10620                 // are set in order to figure out which time zone rules apply and
10621                 // what the offset is at that point in the year
10622                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
10623                 if (this.offset !== 0) {
10624                     this.rd = this.newRd({
10625                         rd: this.rd.getRataDie() - this.offset
10626                     });
10627                 }
10628 
10629                 this._init2(params);
10630             })
10631         });
10632     } else {
10633         this._init2(params);
10634     }
10635 };
10636 
10637 /**
10638  * Finish initializing this date
10639  * @private
10640  */
10641 HebrewDate.prototype._init2 = function (params) {
10642     if (!this.rd) {
10643         this.rd = this.newRd(params);
10644         this._calcDateComponents();
10645     }
10646 
10647     if (typeof(params.onLoad) === "function") {
10648         params.onLoad(this);
10649     }
10650 };
10651 
10652 /**
10653  * the cumulative lengths of each month for a non-leap year, without new years corrections,
10654  * that can be used in reverse to map days to months
10655  * @private
10656  * @const
10657  * @type Array.<number>
10658  */
10659 HebrewDate.cumMonthLengthsReverse = [
10660 //  [days, monthnumber],
10661 	[0,   7],  /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10662 	[30,  8],  /* Heshvan */
10663 	[59,  9],  /* Kislev */
10664 	[88,  10], /* Teveth */
10665 	[117, 11], /* Shevat */
10666 	[147, 12], /* Adar I */
10667 	[176, 1],  /* Nisan */
10668 	[206, 2],  /* Iyyar */
10669 	[235, 3],  /* Sivan */
10670 	[265, 4],  /* Tammuz */
10671 	[294, 5],  /* Av */
10672 	[324, 6],  /* Elul */
10673 	[354, 7]   /* end of year sentinel value */
10674 ];
10675 
10676 /**
10677  * the cumulative lengths of each month for a leap year, without new years corrections
10678  * that can be used in reverse to map days to months
10679  *
10680  * @private
10681  * @const
10682  * @type Array.<number>
10683  */
10684 HebrewDate.cumMonthLengthsLeapReverse = [
10685 //  [days, monthnumber],
10686 	[0,   7],  /* Tishri - Jewish New Year (Rosh HaShanah) starts in month 7 */
10687 	[30,  8],  /* Heshvan */
10688 	[59,  9],  /* Kislev */
10689 	[88,  10], /* Teveth */
10690 	[117, 11], /* Shevat */
10691 	[147, 12], /* Adar I */
10692 	[177, 13], /* Adar II */
10693 	[206, 1],  /* Nisan */
10694 	[236, 2],  /* Iyyar */
10695 	[265, 3],  /* Sivan */
10696 	[295, 4],  /* Tammuz */
10697 	[324, 5],  /* Av */
10698 	[354, 6],  /* Elul */
10699 	[384, 7]   /* end of year sentinel value */
10700 ];
10701 
10702 /**
10703  * Number of days difference between RD 0 of the Hebrew calendar
10704  * (Jan 1, 1 Gregorian = JD 1721057.5) and RD 0 of the Hebrew calendar
10705  * (September 7, -3760 Gregorian = JD 347997.25)
10706  * @private
10707  * @const
10708  * @type number
10709  */
10710 HebrewDate.GregorianDiff = 1373060.25;
10711 
10712 /**
10713  * Return a new RD for this date type using the given params.
10714  * @private
10715  * @param {Object=} params the parameters used to create this rata die instance
10716  * @returns {RataDie} the new RD instance for the given params
10717  */
10718 HebrewDate.prototype.newRd = function (params) {
10719 	return new HebrewRataDie(params);
10720 };
10721 
10722 /**
10723  * Return the year for the given RD
10724  * @protected
10725  * @param {number} rd RD to calculate from
10726  * @returns {number} the year for the RD
10727  */
10728 HebrewDate.prototype._calcYear = function(rd) {
10729 	var year, approximation, nextNewYear;
10730 
10731 	// divide by the average number of days per year in the Hebrew calendar
10732 	// to approximate the year, then tweak it to get the real year
10733 	approximation = Math.floor(rd / 365.246822206) + 1;
10734 
10735 	// console.log("HebrewDate._calcYear: approx is " + approximation);
10736 
10737 	// search forward from approximation-1 for the year that actually contains this rd
10738 	year = approximation;
10739 	nextNewYear = HebrewCal.newYear(year);
10740 	while (rd >= nextNewYear) {
10741 		year++;
10742 		nextNewYear = HebrewCal.newYear(year);
10743 	}
10744 	return year - 1;
10745 };
10746 
10747 /**
10748  * Calculate date components for the given RD date.
10749  * @protected
10750  */
10751 HebrewDate.prototype._calcDateComponents = function () {
10752 	var remainder,
10753 		i,
10754 		table,
10755 		target,
10756 		rd = this.rd.getRataDie();
10757 
10758 	// console.log("HebrewDate.calcComponents: calculating for rd " + rd);
10759 
10760 	if (typeof(this.offset) === "undefined") {
10761 		this.year = this._calcYear(rd);
10762 
10763 		// now offset the RD by the time zone, then recalculate in case we were
10764 		// near the year boundary
10765 		if (!this.tz) {
10766 			this.tz = new TimeZone({id: this.timezone});
10767 		}
10768 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
10769 	}
10770 
10771 	if (this.offset !== 0) {
10772 		rd += this.offset;
10773 		this.year = this._calcYear(rd);
10774 	}
10775 
10776 	// console.log("HebrewDate.calcComponents: year is " + this.year + " with starting rd " + thisNewYear);
10777 
10778 	remainder = rd - HebrewCal.newYear(this.year);
10779 	// console.log("HebrewDate.calcComponents: remainder is " + remainder);
10780 
10781 	// take out new years corrections so we get the right month when we look it up in the table
10782 	if (remainder >= 59) {
10783 		if (remainder >= 88) {
10784 			if (HebrewCal.longKislev(this.year)) {
10785 				remainder--;
10786 			}
10787 		}
10788 		if (HebrewCal.longHeshvan(this.year)) {
10789 			remainder--;
10790 		}
10791 	}
10792 
10793 	// console.log("HebrewDate.calcComponents: after new years corrections, remainder is " + remainder);
10794 
10795 	table = this.cal.isLeapYear(this.year) ?
10796 			HebrewDate.cumMonthLengthsLeapReverse :
10797 			HebrewDate.cumMonthLengthsReverse;
10798 
10799 	i = 0;
10800 	target = Math.floor(remainder);
10801 	while (i+1 < table.length && target >= table[i+1][0]) {
10802 		i++;
10803 	}
10804 
10805 	this.month = table[i][1];
10806 	// console.log("HebrewDate.calcComponents: remainder is " + remainder);
10807 	remainder -= table[i][0];
10808 
10809 	// console.log("HebrewDate.calcComponents: month is " + this.month + " and remainder is " + remainder);
10810 
10811 	this.day = Math.floor(remainder);
10812 	remainder -= this.day;
10813 	this.day++; // days are 1-based
10814 
10815 	// console.log("HebrewDate.calcComponents: day is " + this.day + " and remainder is " + remainder);
10816 
10817 	// now convert to milliseconds for the rest of the calculation
10818 	remainder = Math.round(remainder * 86400000);
10819 
10820 	this.hour = Math.floor(remainder/3600000);
10821 	remainder -= this.hour * 3600000;
10822 
10823 	// the hours from 0 to 6 are actually 18:00 to midnight of the previous
10824 	// gregorian day, so we have to adjust for that
10825 	if (this.hour >= 6) {
10826 		this.hour -= 6;
10827 	} else {
10828 		this.hour += 18;
10829 	}
10830 
10831 	this.minute = Math.floor(remainder/60000);
10832 	remainder -= this.minute * 60000;
10833 
10834 	this.second = Math.floor(remainder/1000);
10835 	remainder -= this.second * 1000;
10836 
10837 	this.millisecond = Math.floor(remainder);
10838 };
10839 
10840 /**
10841  * Return the day of the week of this date. The day of the week is encoded
10842  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
10843  *
10844  * @return {number} the day of the week
10845  */
10846 HebrewDate.prototype.getDayOfWeek = function() {
10847 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
10848 	return MathUtils.mod(rd+1, 7);
10849 };
10850 
10851 /**
10852  * Get the Halaqim (parts) of an hour. There are 1080 parts in an hour, which means
10853  * each part is 3.33333333 seconds long. This means the number returned may not
10854  * be an integer.
10855  *
10856  * @return {number} the halaqim parts of the current hour
10857  */
10858 HebrewDate.prototype.getHalaqim = function() {
10859 	if (this.parts < 0) {
10860 		// convert to ms first, then to parts
10861 		var h = this.minute * 60000 + this.second * 1000 + this.millisecond;
10862 		this.parts = (h * 0.0003);
10863 	}
10864 	return this.parts;
10865 };
10866 
10867 /**
10868  * Return the rd number of the first Sunday of the given ISO year.
10869  * @protected
10870  * @return the rd of the first Sunday of the ISO year
10871  */
10872 HebrewDate.prototype.firstSunday = function (year) {
10873 	var tishri1 = this.newRd({
10874 		year: year,
10875 		month: 7,
10876 		day: 1,
10877 		hour: 18,
10878 		minute: 0,
10879 		second: 0,
10880 		millisecond: 0,
10881 		cal: this.cal
10882 	});
10883 	var firstThu = this.newRd({
10884 		rd: tishri1.onOrAfter(4),
10885 		cal: this.cal
10886 	});
10887 	return firstThu.before(0);
10888 };
10889 
10890 /**
10891  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to
10892  * 385, regardless of months or weeks, etc. That is, Tishri 1st is day 1, and
10893  * Elul 29 is 385 for a leap year with a long Heshvan and long Kislev.
10894  * @return {number} the ordinal day of the year
10895  */
10896 HebrewDate.prototype.getDayOfYear = function() {
10897 	var table = this.cal.isLeapYear(this.year) ?
10898 				HebrewRataDie.cumMonthLengthsLeap :
10899 				HebrewRataDie.cumMonthLengths;
10900 	var days = table[this.month-1];
10901 	if ((this.month < 7 || this.month > 8) && HebrewCal.longHeshvan(this.year)) {
10902 		days++;
10903 	}
10904 	if ((this.month < 7 || this.month > 9) && HebrewCal.longKislev(this.year)) {
10905 		days++;
10906 	}
10907 
10908 	return days + this.day;
10909 };
10910 
10911 /**
10912  * Return the ordinal number of the week within the month. The first week of a month is
10913  * the first one that contains 4 or more days in that month. If any days precede this
10914  * first week, they are marked as being in week 0. This function returns values from 0
10915  * through 6.<p>
10916  *
10917  * The locale is a required parameter because different locales that use the same
10918  * Hebrew calendar consider different days of the week to be the beginning of
10919  * the week. This can affect the week of the month in which some days are located.
10920  *
10921  * @param {Locale|string} locale the locale or locale spec to use when figuring out
10922  * the first day of the week
10923  * @return {number} the ordinal number of the week within the current month
10924  */
10925 HebrewDate.prototype.getWeekOfMonth = function(locale) {
10926 	var li = new LocaleInfo(locale),
10927 		first = this.newRd({
10928 			year: this.year,
10929 			month: this.month,
10930 			day: 1,
10931 			hour: 18,
10932 			minute: 0,
10933 			second: 0,
10934 			millisecond: 0
10935 		}),
10936 		rd = this.rd.getRataDie(),
10937 		weekStart = first.onOrAfter(li.getFirstDayOfWeek());
10938 
10939 	if (weekStart - first.getRataDie() > 3) {
10940 		// if the first week has 4 or more days in it of the current month, then consider
10941 		// that week 1. Otherwise, it is week 0. To make it week 1, move the week start
10942 		// one week earlier.
10943 		weekStart -= 7;
10944 	}
10945 	return (rd < weekStart) ? 0 : Math.floor((rd - weekStart) / 7) + 1;
10946 };
10947 
10948 /**
10949  * Return the era for this date as a number. The value for the era for Hebrew
10950  * calendars is -1 for "before the Hebrew era" and 1 for "the Hebrew era".
10951  * Hebrew era dates are any date after Tishri 1, 1, which is the same as
10952  * September 7, 3760 BC in the Gregorian calendar.
10953  *
10954  * @return {number} 1 if this date is in the Hebrew era, -1 if it is before the
10955  * Hebrew era
10956  */
10957 HebrewDate.prototype.getEra = function() {
10958 	return (this.year < 1) ? -1 : 1;
10959 };
10960 
10961 /**
10962  * Return the name of the calendar that governs this date.
10963  *
10964  * @return {string} a string giving the name of the calendar
10965  */
10966 HebrewDate.prototype.getCalendar = function() {
10967 	return "hebrew";
10968 };
10969 
10970 // register with the factory method
10971 IDate._constructors["hebrew"] = HebrewDate;
10972 
10973 
10974 
10975 /*< IslamicCal.js */
10976 /*
10977  * islamic.js - Represent a Islamic calendar object.
10978  * 
10979  * Copyright © 2012-2015,2018, JEDLSoft
10980  *
10981  * Licensed under the Apache License, Version 2.0 (the "License");
10982  * you may not use this file except in compliance with the License.
10983  * You may obtain a copy of the License at
10984  *
10985  *     http://www.apache.org/licenses/LICENSE-2.0
10986  *
10987  * Unless required by applicable law or agreed to in writing, software
10988  * distributed under the License is distributed on an "AS IS" BASIS,
10989  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10990  *
10991  * See the License for the specific language governing permissions and
10992  * limitations under the License.
10993  */
10994 
10995 
10996 /* !depends 
10997 Calendar.js 
10998 MathUtils.js 
10999 */
11000 
11001 
11002 /**
11003  * @class
11004  * Construct a new Islamic calendar object. This class encodes information about
11005  * the civil Islamic calendar. The civil Islamic calendar is a tabular islamic 
11006  * calendar where the dates are calculated by arithmetic rules. This differs from 
11007  * the religious Islamic calendar which is used to mark the beginning of particular 
11008  * holidays. The religious calendar depends on the first sighting of the new 
11009  * crescent moon to determine the first day of the new month. Because humans and 
11010  * weather are both involved, the actual time of sighting varies, so it is not 
11011  * really possible to precalculate the religious calendar. Certain groups, such 
11012  * as the Islamic Society of North America, decreed in in 2007 that they will use
11013  * a calendar based on calculations rather than observations to determine the 
11014  * beginning of lunar months, and therefore the dates of holidays.<p>
11015  * 
11016  * @param {Object=} options Options governing the construction of this instance
11017  * @constructor
11018  * @extends Calendar
11019  */
11020 var IslamicCal = function(options) {
11021 	this.type = "islamic";
11022     
11023     if (options && typeof(options.onLoad) === "function") {
11024         options.onLoad(this);
11025     }
11026 };
11027 
11028 /**
11029  * the lengths of each month 
11030  * @private
11031  * @const
11032  * @type Array.<number>
11033  */
11034 IslamicCal.monthLengths = [
11035 	30,  /* Muharram */
11036 	29,  /* Saffar */
11037 	30,  /* Rabi'I */
11038 	29,  /* Rabi'II */
11039 	30,  /* Jumada I */
11040 	29,  /* Jumada II */
11041 	30,  /* Rajab */
11042 	29,  /* Sha'ban */
11043 	30,  /* Ramadan */
11044 	29,  /* Shawwal */
11045 	30,  /* Dhu al-Qa'da */
11046 	29   /* Dhu al-Hijja */
11047 ];
11048 
11049 
11050 /**
11051  * Return the number of months in the given year. The number of months in a year varies
11052  * for luni-solar calendars because in some years, an extra month is needed to extend the 
11053  * days in a year to an entire solar year. The month is represented as a 1-based number
11054  * where 1=first month, 2=second month, etc.
11055  * 
11056  * @param {number} year a year for which the number of months is sought
11057  */
11058 IslamicCal.prototype.getNumMonths = function(year) {
11059 	return 12;
11060 };
11061 
11062 /**
11063  * Return the number of days in a particular month in a particular year. This function
11064  * can return a different number for a month depending on the year because of things
11065  * like leap years.
11066  *
11067  * @param {number} month the month for which the length is sought
11068  * @param {number} year the year within which that month can be found
11069  * @return {number} the number of days within the given month in the given year
11070  */
11071 IslamicCal.prototype.getMonLength = function(month, year) {
11072 	if (month !== 12) {
11073 		return IslamicCal.monthLengths[month-1];
11074 	} else {
11075 		return this.isLeapYear(year) ? 30 : 29;
11076 	}
11077 };
11078 
11079 /**
11080  * Return true if the given year is a leap year in the Islamic calendar.
11081  * The year parameter may be given as a number, or as a IslamicDate object.
11082  * @param {number} year the year for which the leap year information is being sought
11083  * @return {boolean} true if the given year is a leap year
11084  */
11085 IslamicCal.prototype.isLeapYear = function(year) {
11086 	return (MathUtils.mod((14 + 11 * year), 30) < 11);
11087 };
11088 
11089 /**
11090  * Return the type of this calendar.
11091  * 
11092  * @return {string} the name of the type of this calendar 
11093  */
11094 IslamicCal.prototype.getType = function() {
11095 	return this.type;
11096 };
11097 
11098 
11099 /*register this calendar for the factory method */
11100 Calendar._constructors["islamic"] = IslamicCal;
11101 
11102 
11103 
11104 /*< SearchUtils.js */
11105 /*
11106  * SearchUtils.js - Misc search utility routines
11107  * 
11108  * Copyright © 2013-2015, JEDLSoft
11109  *
11110  * Licensed under the Apache License, Version 2.0 (the "License");
11111  * you may not use this file except in compliance with the License.
11112  * You may obtain a copy of the License at
11113  *
11114  *     http://www.apache.org/licenses/LICENSE-2.0
11115  *
11116  * Unless required by applicable law or agreed to in writing, software
11117  * distributed under the License is distributed on an "AS IS" BASIS,
11118  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11119  *
11120  * See the License for the specific language governing permissions and
11121  * limitations under the License.
11122  */
11123 
11124 var SearchUtils = {};
11125 
11126 /**
11127  * Binary search a sorted array for a particular target value.
11128  * If the exact value is not found, it returns the index of the smallest 
11129  * entry that is greater than the given target value.<p> 
11130  * 
11131  * The comparator
11132  * parameter is a function that knows how to compare elements of the 
11133  * array and the target. The function should return a value greater than 0
11134  * if the array element is greater than the target, a value less than 0 if
11135  * the array element is less than the target, and 0 if the array element 
11136  * and the target are equivalent.<p>
11137  * 
11138  * If the comparator function is not specified, this function assumes
11139  * the array and the target are numeric values and should be compared 
11140  * as such.<p>
11141  * 
11142  * 
11143  * @static
11144  * @param {*} target element being sought 
11145  * @param {Array} arr the array being searched
11146  * @param {?function(*,*)=} comparator a comparator that is appropriate for comparing two entries
11147  * in the array  
11148  * @return the index of the array into which the value would fit if 
11149  * inserted, or -1 if given array is not an array or the target is not 
11150  * a number
11151  */
11152 SearchUtils.bsearch = function(target, arr, comparator) {
11153 	if (typeof(arr) === 'undefined' || !arr || typeof(target) === 'undefined') {
11154 		return -1;
11155 	}
11156 	
11157 	var high = arr.length - 1,
11158 		low = 0,
11159 		mid = 0,
11160 		value,
11161 		cmp = comparator || SearchUtils.bsearch.numbers;
11162 	
11163 	while (low <= high) {
11164 		mid = Math.floor((high+low)/2);
11165 		value = cmp(arr[mid], target);
11166 		if (value > 0) {
11167 			high = mid - 1;
11168 		} else if (value < 0) {
11169 			low = mid + 1;
11170 		} else {
11171 			return mid;
11172 		}
11173 	}
11174 	
11175 	return low;
11176 };
11177 
11178 /**
11179  * Returns whether or not the given element is greater than, less than,
11180  * or equal to the given target.<p>
11181  * 
11182  * @private
11183  * @static
11184  * @param {number} element the element being tested
11185  * @param {number} target the target being sought
11186  */
11187 SearchUtils.bsearch.numbers = function(element, target) {
11188 	return element - target;
11189 };
11190 
11191 /**
11192  * Do a bisection search of a function for a particular target value.<p> 
11193  * 
11194  * The function to search is a function that takes a numeric parameter, 
11195  * does calculations, and returns gives a numeric result. The 
11196  * function should should be smooth and not have any discontinuities 
11197  * between the low and high values of the parameter.
11198  *  
11199  * 
11200  * @static
11201  * @param {number} target value being sought
11202  * @param {number} low the lower bounds to start searching
11203  * @param {number} high the upper bounds to start searching
11204  * @param {number} precision minimum precision to support. Use 0 if you want to use the default.
11205  * @param {?function(number)=} func function to search 
11206  * @return an approximation of the input value to the function that gives the desired
11207  * target output value, correct to within the error range of Javascript floating point 
11208  * arithmetic, or NaN if there was some error
11209  */
11210 SearchUtils.bisectionSearch = function(target, low, high, precision, func) {
11211 	if (typeof(target) !== 'number' || 
11212 			typeof(low) !== 'number' || 
11213 			typeof(high) !== 'number' || 
11214 			typeof(func) !== 'function') {
11215 		return NaN;
11216 	}
11217 	
11218 	var mid = 0,
11219 		value,
11220 		pre = precision > 0 ? precision : 1e-13;
11221 	
11222 	do {
11223 		mid = (high+low)/2;
11224 		value = func(mid);
11225 		if (value > target) {
11226 			high = mid;
11227 		} else if (value < target) {
11228 			low = mid;
11229 		}
11230 	} while (high - low > pre);
11231 	
11232 	return mid;
11233 };
11234 
11235 
11236 
11237 /*< IslamicRataDie.js */
11238 /*
11239  * IslamicRataDie.js - Represent an RD date in the Islamic calendar
11240  * 
11241  * Copyright © 2012-2015, JEDLSoft
11242  *
11243  * Licensed under the Apache License, Version 2.0 (the "License");
11244  * you may not use this file except in compliance with the License.
11245  * You may obtain a copy of the License at
11246  *
11247  *     http://www.apache.org/licenses/LICENSE-2.0
11248  *
11249  * Unless required by applicable law or agreed to in writing, software
11250  * distributed under the License is distributed on an "AS IS" BASIS,
11251  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11252  *
11253  * See the License for the specific language governing permissions and
11254  * limitations under the License.
11255  */
11256 
11257 /* !depends 
11258 IslamicCal.js
11259 RataDie.js
11260 */
11261 
11262 
11263 /**
11264  * @class
11265  * Construct a new Islamic RD date number object. The constructor parameters can 
11266  * contain any of the following properties:
11267  * 
11268  * <ul>
11269  * <li><i>unixtime<i> - sets the time of this instance according to the given 
11270  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
11271  * 
11272  * <li><i>julianday</i> - sets the time of this instance according to the given
11273  * Julian Day instance or the Julian Day given as a float
11274  * 
11275  * <li><i>year</i> - any integer, including 0
11276  * 
11277  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
11278  * 
11279  * <li><i>day</i> - 1 to 31
11280  * 
11281  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
11282  * is always done with an unambiguous 24 hour representation
11283  * 
11284  * <li><i>minute</i> - 0 to 59
11285  * 
11286  * <li><i>second</i> - 0 to 59
11287  * 
11288  * <li><i>millisecond</i> - 0 to 999
11289  * 
11290  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
11291  * </ul>
11292  *
11293  * If the constructor is called with another Islamic date instance instead of
11294  * a parameter block, the other instance acts as a parameter block and its
11295  * settings are copied into the current instance.<p>
11296  * 
11297  * If the constructor is called with no arguments at all or if none of the 
11298  * properties listed above are present, then the RD is calculate based on 
11299  * the current date at the time of instantiation. <p>
11300  * 
11301  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
11302  * specified in the params, it is assumed that they have the smallest possible
11303  * value in the range for the property (zero or one).<p>
11304  * 
11305  * 
11306  * @private
11307  * @constructor
11308  * @extends RataDie
11309  * @param {Object=} params parameters that govern the settings and behaviour of this Islamic RD date
11310  */
11311 var IslamicRataDie = function(params) {
11312 	this.cal = params && params.cal || new IslamicCal();
11313 	this.rd = NaN;
11314 	RataDie.call(this, params);
11315 };
11316 
11317 IslamicRataDie.prototype = new RataDie();
11318 IslamicRataDie.prototype.parent = RataDie;
11319 IslamicRataDie.prototype.constructor = IslamicRataDie;
11320 
11321 /**
11322  * The difference between a zero Julian day and the first Islamic date
11323  * of Friday, July 16, 622 CE Julian. 
11324  * @private
11325  * @type number
11326  */
11327 IslamicRataDie.prototype.epoch = 1948439.5;
11328 
11329 /**
11330  * Calculate the Rata Die (fixed day) number of the given date from the
11331  * date components.
11332  *
11333  * @protected
11334  * @param {Object} date the date components to calculate the RD from
11335  */
11336 IslamicRataDie.prototype._setDateComponents = function(date) {
11337 	var days = (date.year - 1) * 354 +
11338 		Math.ceil(29.5 * (date.month - 1)) +
11339 		date.day +
11340 		Math.floor((3 + 11 * date.year) / 30) - 1;
11341 	var time = (date.hour * 3600000 +
11342 		date.minute * 60000 +
11343 		date.second * 1000 +
11344 		date.millisecond) / 
11345 		86400000; 
11346 	
11347 	//console.log("getRataDie: converting " +  JSON.stringify(date));
11348 	//console.log("getRataDie: days is " +  days);
11349 	//console.log("getRataDie: time is " +  time);
11350 	//console.log("getRataDie: rd is " +  (days + time));
11351 
11352 	this.rd = days + time;
11353 };
11354 	
11355 
11356 /*< IslamicDate.js */
11357 /*
11358  * islamicDate.js - Represent a date in the Islamic calendar
11359  * 
11360  * Copyright © 2012-2015, JEDLSoft
11361  *
11362  * Licensed under the Apache License, Version 2.0 (the "License");
11363  * you may not use this file except in compliance with the License.
11364  * You may obtain a copy of the License at
11365  *
11366  *     http://www.apache.org/licenses/LICENSE-2.0
11367  *
11368  * Unless required by applicable law or agreed to in writing, software
11369  * distributed under the License is distributed on an "AS IS" BASIS,
11370  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11371  *
11372  * See the License for the specific language governing permissions and
11373  * limitations under the License.
11374  */
11375 
11376 /* !depends 
11377 ilib.js
11378 Locale.js
11379 LocaleInfo.js
11380 TimeZone.js
11381 IDate.js
11382 MathUtils.js
11383 SearchUtils.js
11384 IslamicCal.js
11385 IslamicRataDie.js
11386 */
11387 
11388 
11389 
11390 
11391 /**
11392  * @class
11393  * Construct a new civil Islamic date object. The constructor can be called
11394  * with a params object that can contain the following properties:<p>
11395  * 
11396  * <ul>
11397  * <li><i>julianday</i> - the Julian Day to set into this date
11398  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero year
11399  * <li><i>month</i> - 1 to 12, where 1 means Muharram, 2 means Saffar, etc.
11400  * <li><i>day</i> - 1 to 30
11401  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
11402  * is always done with an unambiguous 24 hour representation
11403  * <li><i>minute</i> - 0 to 59
11404  * <li><i>second</i> - 0 to 59
11405  * <li><i>millisecond</i> - 0 to 999
11406  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
11407  * of this julian date. The date/time is kept in the local time. The time zone
11408  * is used later if this date is formatted according to a different time zone and
11409  * the difference has to be calculated, or when the date format has a time zone
11410  * component in it.
11411  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
11412  * given, it can be inferred from this locale. For locales that span multiple
11413  * time zones, the one with the largest population is chosen as the one that 
11414  * represents the locale. 
11415  * 
11416  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
11417  * </ul>
11418  * 
11419  * If called with another Islamic date argument, the date components of the given
11420  * date are copied into the current one.<p>
11421  * 
11422  * If the constructor is called with no arguments at all or if none of the 
11423  * properties listed above 
11424  * from <i>julianday</i> through <i>millisecond</i> are present, then the date 
11425  * components are 
11426  * filled in with the current date at the time of instantiation. Note that if
11427  * you do not give the time zone when defaulting to the current time and the 
11428  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
11429  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
11430  * Mean Time").<p>
11431  * 
11432  * 
11433  * @constructor
11434  * @extends IDate
11435  * @param {Object=} params parameters that govern the settings and behaviour of this Islamic date
11436  */
11437 var IslamicDate = function(params) {
11438     this.cal = new IslamicCal();
11439 
11440     params = params || {};
11441 
11442     if (params.timezone) {
11443         this.timezone = params.timezone;
11444     }
11445     if (params.locale) {
11446         this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
11447     }
11448 
11449     if (!this.timezone) {
11450         if (this.locale) {
11451             new LocaleInfo(this.locale, {
11452                 sync: params.sync,
11453                 loadParams: params.loadParams,
11454                 onLoad: ilib.bind(this, function(li) {
11455                     this.li = li;
11456                     this.timezone = li.getTimeZone();
11457                     this._init(params);
11458                 })
11459             });
11460         } else {
11461             this.timezone = "local";
11462             this._init(params);
11463         }
11464     } else {
11465         this._init(params);
11466     }
11467 };
11468 
11469 IslamicDate.prototype = new IDate({noinstance: true});
11470 IslamicDate.prototype.parent = IDate;
11471 IslamicDate.prototype.constructor = IslamicDate;
11472 
11473 /**
11474  * Initialize the date
11475  * @private
11476  */
11477 IslamicDate.prototype._init = function (params) {
11478     if (params.year || params.month || params.day || params.hour ||
11479         params.minute || params.second || params.millisecond ) {
11480         /**
11481          * Year in the Islamic calendar.
11482          * @type number
11483          */
11484         this.year = parseInt(params.year, 10) || 0;
11485 
11486         /**
11487          * The month number, ranging from 1 to 12 (December).
11488          * @type number
11489          */
11490         this.month = parseInt(params.month, 10) || 1;
11491 
11492         /**
11493          * The day of the month. This ranges from 1 to 30.
11494          * @type number
11495          */
11496         this.day = parseInt(params.day, 10) || 1;
11497 
11498         /**
11499          * The hour of the day. This can be a number from 0 to 23, as times are
11500          * stored unambiguously in the 24-hour clock.
11501          * @type number
11502          */
11503         this.hour = parseInt(params.hour, 10) || 0;
11504 
11505         /**
11506          * The minute of the hours. Ranges from 0 to 59.
11507          * @type number
11508          */
11509         this.minute = parseInt(params.minute, 10) || 0;
11510 
11511         /**
11512          * The second of the minute. Ranges from 0 to 59.
11513          * @type number
11514          */
11515         this.second = parseInt(params.second, 10) || 0;
11516 
11517         /**
11518          * The millisecond of the second. Ranges from 0 to 999.
11519          * @type number
11520          */
11521         this.millisecond = parseInt(params.millisecond, 10) || 0;
11522 
11523         /**
11524          * The day of the year. Ranges from 1 to 355.
11525          * @type number
11526          */
11527         this.dayOfYear = parseInt(params.dayOfYear, 10);
11528 
11529         if (typeof(params.dst) === 'boolean') {
11530             this.dst = params.dst;
11531         }
11532 
11533         this.rd = this.newRd(this);
11534 
11535         new TimeZone({
11536             id: this.timezone,
11537             sync: params.sync,
11538             loadParams: params.loadParams,
11539             onLoad: ilib.bind(this, function(tz) {
11540                 this.tz = tz;
11541                 // add the time zone offset to the rd to convert to UTC
11542                 // getOffsetMillis requires that this.year, this.rd, and this.dst 
11543                 // are set in order to figure out which time zone rules apply and 
11544                 // what the offset is at that point in the year
11545                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
11546                 if (this.offset !== 0) {
11547                     this.rd = this.newRd({
11548                         rd: this.rd.getRataDie() - this.offset
11549                     });
11550                 }
11551                 this._init2(params);
11552             })
11553         });
11554     } else {
11555         this._init2(params);
11556     }
11557 };
11558 
11559 /**
11560  * @private
11561  * Finish initializing this date object
11562  */
11563 IslamicDate.prototype._init2 = function (params) {
11564     if (!this.rd) {
11565         this.rd = this.newRd(params);
11566         this._calcDateComponents();
11567     }
11568 
11569     if (typeof(params.onLoad) === "function") {
11570         params.onLoad(this);
11571     }
11572 };
11573 
11574 /**
11575  * the cumulative lengths of each month, for a non-leap year 
11576  * @private
11577  * @const
11578  * @type Array.<number>
11579  */
11580 IslamicDate.cumMonthLengths = [
11581 	0,  /* Muharram */
11582 	30,  /* Saffar */
11583 	59,  /* Rabi'I */
11584 	89,  /* Rabi'II */
11585 	118,  /* Jumada I */
11586 	148,  /* Jumada II */
11587 	177,  /* Rajab */
11588 	207,  /* Sha'ban */
11589 	236,  /* Ramadan */
11590 	266,  /* Shawwal */
11591 	295,  /* Dhu al-Qa'da */
11592 	325,  /* Dhu al-Hijja */
11593 	354
11594 ];
11595 
11596 /**
11597  * Number of days difference between RD 0 of the Gregorian calendar and
11598  * RD 0 of the Islamic calendar. 
11599  * @private
11600  * @const
11601  * @type number
11602  */
11603 IslamicDate.GregorianDiff = 227015;
11604 
11605 /**
11606  * Return a new RD for this date type using the given params.
11607  * @protected
11608  * @param {Object=} params the parameters used to create this rata die instance
11609  * @returns {RataDie} the new RD instance for the given params
11610  */
11611 IslamicDate.prototype.newRd = function (params) {
11612 	return new IslamicRataDie(params);
11613 };
11614 
11615 /**
11616  * Return the year for the given RD
11617  * @protected
11618  * @param {number} rd RD to calculate from 
11619  * @returns {number} the year for the RD
11620  */
11621 IslamicDate.prototype._calcYear = function(rd) {
11622 	return Math.floor((30 * rd + 10646) / 10631);
11623 };
11624 
11625 /**
11626  * Calculate date components for the given RD date.
11627  * @protected
11628  */
11629 IslamicDate.prototype._calcDateComponents = function () {
11630 	var remainder,
11631 		rd = this.rd.getRataDie();
11632 	
11633 	this.year = this._calcYear(rd);
11634 
11635 	if (typeof(this.offset) === "undefined") {
11636 		this.year = this._calcYear(rd);
11637 		
11638 		// now offset the RD by the time zone, then recalculate in case we were 
11639 		// near the year boundary
11640 		if (!this.tz) {
11641 			this.tz = new TimeZone({id: this.timezone});
11642 		}
11643 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
11644 	}
11645 
11646 	if (this.offset !== 0) {
11647 		rd += this.offset;
11648 		this.year = this._calcYear(rd);
11649 	}
11650 
11651 	//console.log("IslamicDate.calcComponent: calculating for rd " + rd);
11652 	//console.log("IslamicDate.calcComponent: year is " + ret.year);
11653 	var yearStart = this.newRd({
11654 		year: this.year,
11655 		month: 1,
11656 		day: 1,
11657 		hour: 0,
11658 		minute: 0,
11659 		second: 0,
11660 		millisecond: 0
11661 	});
11662 	remainder = rd - yearStart.getRataDie() + 1;
11663 	
11664 	this.dayOfYear = remainder;
11665 	
11666 	//console.log("IslamicDate.calcComponent: remainder is " + remainder);
11667 	
11668 	this.month = SearchUtils.bsearch(remainder, IslamicDate.cumMonthLengths);
11669 	remainder -= IslamicDate.cumMonthLengths[this.month-1];
11670 
11671 	//console.log("IslamicDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
11672 	
11673 	this.day = Math.floor(remainder);
11674 	remainder -= this.day;
11675 
11676 	//console.log("IslamicDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
11677 
11678 	// now convert to milliseconds for the rest of the calculation
11679 	remainder = Math.round(remainder * 86400000);
11680 	
11681 	this.hour = Math.floor(remainder/3600000);
11682 	remainder -= this.hour * 3600000;
11683 	
11684 	this.minute = Math.floor(remainder/60000);
11685 	remainder -= this.minute * 60000;
11686 	
11687 	this.second = Math.floor(remainder/1000);
11688 	remainder -= this.second * 1000;
11689 	
11690 	this.millisecond = remainder;
11691 };
11692 
11693 /**
11694  * Return the day of the week of this date. The day of the week is encoded
11695  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
11696  * 
11697  * @return {number} the day of the week
11698  */
11699 IslamicDate.prototype.getDayOfWeek = function() {
11700 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
11701 	return MathUtils.mod(rd-2, 7);
11702 };
11703 
11704 /**
11705  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
11706  * 354 or 355, regardless of months or weeks, etc. That is, Muharran 1st is day 1, and 
11707  * Dhu al-Hijja 29 is 354.
11708  * @return {number} the ordinal day of the year
11709  */
11710 IslamicDate.prototype.getDayOfYear = function() {
11711 	return IslamicDate.cumMonthLengths[this.month-1] + this.day;
11712 };
11713 
11714 /**
11715  * Return the era for this date as a number. The value for the era for Islamic 
11716  * calendars is -1 for "before the Islamic era" and 1 for "the Islamic era". 
11717  * Islamic era dates are any date after Muharran 1, 1, which is the same as
11718  * July 16, 622 CE in the Gregorian calendar. 
11719  * 
11720  * @return {number} 1 if this date is in the common era, -1 if it is before the 
11721  * common era 
11722  */
11723 IslamicDate.prototype.getEra = function() {
11724 	return (this.year < 1) ? -1 : 1;
11725 };
11726 
11727 /**
11728  * Return the name of the calendar that governs this date.
11729  * 
11730  * @return {string} a string giving the name of the calendar
11731  */
11732 IslamicDate.prototype.getCalendar = function() {
11733 	return "islamic";
11734 };
11735 
11736 //register with the factory method
11737 IDate._constructors["islamic"] = IslamicDate;
11738 
11739 
11740 /*< JulianCal.js */
11741 /*
11742  * julian.js - Represent a Julian calendar object.
11743  * 
11744  * Copyright © 2012-2015,2018, JEDLSoft
11745  *
11746  * Licensed under the Apache License, Version 2.0 (the "License");
11747  * you may not use this file except in compliance with the License.
11748  * You may obtain a copy of the License at
11749  *
11750  *     http://www.apache.org/licenses/LICENSE-2.0
11751  *
11752  * Unless required by applicable law or agreed to in writing, software
11753  * distributed under the License is distributed on an "AS IS" BASIS,
11754  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11755  *
11756  * See the License for the specific language governing permissions and
11757  * limitations under the License.
11758  */
11759 
11760 
11761 /* !depends Calendar.js MathUtils.js */
11762 
11763 
11764 /**
11765  * @class
11766  * Construct a new Julian calendar object. This class encodes information about
11767  * a Julian calendar.<p>
11768  * 
11769  * @param {Object=} options Options governing the construction of this instance
11770  * @constructor
11771  * @extends Calendar
11772  */
11773 var JulianCal = function(options) {
11774 	this.type = "julian";
11775     
11776     if (options && typeof(options.onLoad) === "function") {
11777         options.onLoad(this);
11778     }
11779 };
11780 
11781 /* the lengths of each month */
11782 JulianCal.monthLengths = [
11783 	31,  /* Jan */
11784 	28,  /* Feb */
11785 	31,  /* Mar */
11786 	30,  /* Apr */
11787 	31,  /* May */
11788 	30,  /* Jun */
11789 	31,  /* Jul */
11790 	31,  /* Aug */
11791 	30,  /* Sep */
11792 	31,  /* Oct */
11793 	30,  /* Nov */
11794 	31   /* Dec */
11795 ];
11796 
11797 /**
11798  * the cumulative lengths of each month, for a non-leap year 
11799  * @private
11800  * @const
11801  * @type Array.<number>
11802  */
11803 JulianCal.cumMonthLengths = [
11804     0,   /* Jan */
11805 	31,  /* Feb */
11806 	59,  /* Mar */
11807 	90,  /* Apr */
11808 	120, /* May */
11809 	151, /* Jun */
11810 	181, /* Jul */
11811 	212, /* Aug */
11812 	243, /* Sep */
11813 	273, /* Oct */
11814 	304, /* Nov */
11815 	334, /* Dec */
11816 	365
11817 ];
11818 
11819 /**
11820  * the cumulative lengths of each month, for a leap year 
11821  * @private
11822  * @const
11823  * @type Array.<number>
11824  */
11825 JulianCal.cumMonthLengthsLeap = [
11826 	0,   /* Jan */
11827 	31,  /* Feb */
11828 	60,  /* Mar */
11829 	91,  /* Apr */
11830 	121, /* May */
11831 	152, /* Jun */
11832 	182, /* Jul */
11833 	213, /* Aug */
11834 	244, /* Sep */
11835 	274, /* Oct */
11836 	305, /* Nov */
11837 	335, /* Dec */
11838 	366
11839 ];
11840 
11841 /**
11842  * Return the number of months in the given year. The number of months in a year varies
11843  * for lunar calendars because in some years, an extra month is needed to extend the 
11844  * days in a year to an entire solar year. The month is represented as a 1-based number
11845  * where 1=Jaunary, 2=February, etc. until 12=December.
11846  * 
11847  * @param {number} year a year for which the number of months is sought
11848  */
11849 JulianCal.prototype.getNumMonths = function(year) {
11850 	return 12;
11851 };
11852 
11853 /**
11854  * Return the number of days in a particular month in a particular year. This function
11855  * can return a different number for a month depending on the year because of things
11856  * like leap years.
11857  * 
11858  * @param {number} month the month for which the length is sought
11859  * @param {number} year the year within which that month can be found
11860  * @return {number} the number of days within the given month in the given year
11861  */
11862 JulianCal.prototype.getMonLength = function(month, year) {
11863 	if (month !== 2 || !this.isLeapYear(year)) {
11864 		return JulianCal.monthLengths[month-1];
11865 	} else {
11866 		return 29;
11867 	}
11868 };
11869 
11870 /**
11871  * Return true if the given year is a leap year in the Julian calendar.
11872  * The year parameter may be given as a number, or as a JulDate object.
11873  * @param {number|JulianDate} year the year for which the leap year information is being sought
11874  * @return {boolean} true if the given year is a leap year
11875  */
11876 JulianCal.prototype.isLeapYear = function(year) {
11877 	var y = (typeof(year) === 'number' ? year : year.year);
11878 	return MathUtils.mod(y, 4) === ((year > 0) ? 0 : 3);
11879 };
11880 
11881 /**
11882  * Return the type of this calendar.
11883  * 
11884  * @return {string} the name of the type of this calendar 
11885  */
11886 JulianCal.prototype.getType = function() {
11887 	return this.type;
11888 };
11889 
11890 
11891 /* register this calendar for the factory method */
11892 Calendar._constructors["julian"] = JulianCal;
11893 
11894 
11895 
11896 /*< JulianRataDie.js */
11897 /*
11898  * julianDate.js - Represent a date in the Julian calendar
11899  * 
11900  * Copyright © 2012-2015, JEDLSoft
11901  *
11902  * Licensed under the Apache License, Version 2.0 (the "License");
11903  * you may not use this file except in compliance with the License.
11904  * You may obtain a copy of the License at
11905  *
11906  *     http://www.apache.org/licenses/LICENSE-2.0
11907  *
11908  * Unless required by applicable law or agreed to in writing, software
11909  * distributed under the License is distributed on an "AS IS" BASIS,
11910  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11911  *
11912  * See the License for the specific language governing permissions and
11913  * limitations under the License.
11914  */
11915 
11916 /* !depends 
11917 JulianCal.js 
11918 RataDie.js
11919 */
11920 
11921 
11922 /**
11923  * @class
11924  * Construct a new Julian RD date number object. The constructor parameters can 
11925  * contain any of the following properties:
11926  * 
11927  * <ul>
11928  * <li><i>unixtime<i> - sets the time of this instance according to the given 
11929  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
11930  * 
11931  * <li><i>julianday</i> - sets the time of this instance according to the given
11932  * Julian Day instance or the Julian Day given as a float
11933  * 
11934  * <li><i>year</i> - any integer, including 0
11935  * 
11936  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
11937  * 
11938  * <li><i>day</i> - 1 to 31
11939  * 
11940  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
11941  * is always done with an unambiguous 24 hour representation
11942  * 
11943  * <li><i>minute</i> - 0 to 59
11944  * 
11945  * <li><i>second</i> - 0 to 59
11946  * 
11947  * <li><i>millisecond</i> - 0 to 999
11948  * 
11949  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
11950  * </ul>
11951  *
11952  * If the constructor is called with another Julian date instance instead of
11953  * a parameter block, the other instance acts as a parameter block and its
11954  * settings are copied into the current instance.<p>
11955  * 
11956  * If the constructor is called with no arguments at all or if none of the 
11957  * properties listed above are present, then the RD is calculate based on 
11958  * the current date at the time of instantiation. <p>
11959  * 
11960  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
11961  * specified in the params, it is assumed that they have the smallest possible
11962  * value in the range for the property (zero or one).<p>
11963  * 
11964  * 
11965  * @private
11966  * @constructor
11967  * @extends RataDie
11968  * @param {Object=} params parameters that govern the settings and behaviour of this Julian RD date
11969  */
11970 var JulianRataDie = function(params) {
11971 	this.cal = params && params.cal || new JulianCal();
11972 	this.rd = NaN;
11973 	RataDie.call(this, params);
11974 };
11975 
11976 JulianRataDie.prototype = new RataDie();
11977 JulianRataDie.prototype.parent = RataDie;
11978 JulianRataDie.prototype.constructor = JulianRataDie;
11979 
11980 /**
11981  * The difference between a zero Julian day and the first Julian date
11982  * of Friday, July 16, 622 CE Julian. 
11983  * @private
11984  * @type number
11985  */
11986 JulianRataDie.prototype.epoch = 1721422.5;
11987 
11988 /**
11989  * Calculate the Rata Die (fixed day) number of the given date from the
11990  * date components.
11991  * 
11992  * @protected
11993  * @param {Object} date the date components to calculate the RD from
11994  */
11995 JulianRataDie.prototype._setDateComponents = function(date) {
11996 	var year = date.year + ((date.year < 0) ? 1 : 0);
11997 	var years = 365 * (year - 1) + Math.floor((year-1)/4);
11998 	var dayInYear = (date.month > 1 ? JulianCal.cumMonthLengths[date.month-1] : 0) +
11999 		date.day +
12000 		(this.cal.isLeapYear(date.year) && date.month > 2 ? 1 : 0);
12001 	var rdtime = (date.hour * 3600000 +
12002 		date.minute * 60000 +
12003 		date.second * 1000 +
12004 		date.millisecond) / 
12005 		86400000;
12006 	
12007 	/*
12008 	console.log("calcRataDie: converting " +  JSON.stringify(parts));
12009 	console.log("getRataDie: year is " +  years);
12010 	console.log("getRataDie: day in year is " +  dayInYear);
12011 	console.log("getRataDie: rdtime is " +  rdtime);
12012 	console.log("getRataDie: rd is " +  (years + dayInYear + rdtime));
12013 	*/
12014 	
12015 	this.rd = years + dayInYear + rdtime;
12016 };
12017 
12018 
12019 /*< JulianDate.js */
12020 /*
12021  * JulianDate.js - Represent a date in the Julian calendar
12022  * 
12023  * Copyright © 2012-2015, JEDLSoft
12024  *
12025  * Licensed under the Apache License, Version 2.0 (the "License");
12026  * you may not use this file except in compliance with the License.
12027  * You may obtain a copy of the License at
12028  *
12029  *     http://www.apache.org/licenses/LICENSE-2.0
12030  *
12031  * Unless required by applicable law or agreed to in writing, software
12032  * distributed under the License is distributed on an "AS IS" BASIS,
12033  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12034  *
12035  * See the License for the specific language governing permissions and
12036  * limitations under the License.
12037  */
12038 
12039 /* !depends 
12040 ilib.js
12041 Locale.js
12042 IDate.js 
12043 TimeZone.js
12044 JulianCal.js 
12045 SearchUtils.js 
12046 MathUtils.js
12047 LocaleInfo.js 
12048 JulianRataDie.js
12049 */
12050 
12051 
12052 
12053 
12054 /**
12055  * @class
12056  * Construct a new date object for the Julian Calendar. The constructor can be called
12057  * with a parameter object that contains any of the following properties:
12058  * 
12059  * <ul>
12060  * <li><i>unixtime<i> - sets the time of this instance according to the given 
12061  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
12062  * <li><i>julianday</i> - the Julian Day to set into this date
12063  * <li><i>year</i> - any integer except 0. Years go from -1 (BCE) to 1 (CE), skipping the zero 
12064  * year which doesn't exist in the Julian calendar
12065  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12066  * <li><i>day</i> - 1 to 31
12067  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
12068  * is always done with an unambiguous 24 hour representation
12069  * <li><i>minute</i> - 0 to 59
12070  * <li><i>second</i> - 0 to 59
12071  * <li><i>millisecond<i> - 0 to 999
12072  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
12073  * of this julian date. The date/time is kept in the local time. The time zone
12074  * is used later if this date is formatted according to a different time zone and
12075  * the difference has to be calculated, or when the date format has a time zone
12076  * component in it.
12077  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
12078  * given, it can be inferred from this locale. For locales that span multiple
12079  * time zones, the one with the largest population is chosen as the one that 
12080  * represents the locale. 
12081  * 
12082  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
12083  * </ul>
12084  * 
12085  * NB. The <a href="http://en.wikipedia.org/wiki/Julian_date">Julian Day</a> 
12086  * (JulianDay) object is a <i>different</i> object than a 
12087  * <a href="http://en.wikipedia.org/wiki/Julian_calendar">date in
12088  * the Julian calendar</a> and the two are not to be confused. The Julian Day 
12089  * object represents time as a number of whole and fractional days since the 
12090  * beginning of the epoch, whereas a date in the Julian 
12091  * calendar is a regular date that signifies year, month, day, etc. using the rules
12092  * of the Julian calendar. The naming of Julian Days and the Julian calendar are
12093  * unfortunately close, and come from history.<p>
12094  *  
12095  * If called with another Julian date argument, the date components of the given
12096  * date are copied into the current one.<p>
12097  * 
12098  * If the constructor is called with no arguments at all or if none of the 
12099  * properties listed above 
12100  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
12101  * components are 
12102  * filled in with the current date at the time of instantiation. Note that if
12103  * you do not give the time zone when defaulting to the current time and the 
12104  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
12105  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
12106  * Mean Time").<p>
12107  * 
12108  * 
12109  * @constructor
12110  * @extends IDate
12111  * @param {Object=} params parameters that govern the settings and behaviour of this Julian date
12112  */
12113 var JulianDate = function(params) {
12114 	this.cal = new JulianCal();
12115 	
12116 	params = params || {};
12117 	
12118 	if (params.timezone) {
12119 	    this.timezone = params.timezone;
12120 	}
12121 	if (params.locale) {
12122 	    this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
12123 	}
12124 
12125 	if (!this.timezone) {
12126 	    if (this.locale) {
12127 	        new LocaleInfo(this.locale, {
12128 	            sync: params.sync,
12129 	            loadParams: params.loadParams,
12130 	            onLoad: ilib.bind(this, function(li) {
12131 	                this.li = li;
12132 	                this.timezone = li.getTimeZone();
12133 	                this._init(params);
12134 	            })
12135 	        });
12136 	    } else {
12137 	        this.timezone = "local";
12138 	        this._init(params);
12139 	    }
12140 	} else {
12141 	    this._init(params);
12142 	}
12143 
12144 };
12145 
12146 JulianDate.prototype = new IDate({noinstance: true});
12147 JulianDate.prototype.parent = IDate;
12148 JulianDate.prototype.constructor = JulianDate;
12149 
12150 /**
12151  * @private
12152  * Initialize the date
12153  */
12154 JulianDate.prototype._init = function (params) {
12155     if (params.year || params.month || params.day || params.hour ||
12156         params.minute || params.second || params.millisecond ) {
12157         /**
12158          * Year in the Julian calendar.
12159          * @type number
12160          */
12161         this.year = parseInt(params.year, 10) || 0;
12162         /**
12163          * The month number, ranging from 1 (January) to 12 (December).
12164          * @type number
12165          */
12166         this.month = parseInt(params.month, 10) || 1;
12167         /**
12168          * The day of the month. This ranges from 1 to 31.
12169          * @type number
12170          */
12171         this.day = parseInt(params.day, 10) || 1;
12172         /**
12173          * The hour of the day. This can be a number from 0 to 23, as times are
12174          * stored unambiguously in the 24-hour clock.
12175          * @type number
12176          */
12177         this.hour = parseInt(params.hour, 10) || 0;
12178         /**
12179          * The minute of the hours. Ranges from 0 to 59.
12180          * @type number
12181          */
12182         this.minute = parseInt(params.minute, 10) || 0;
12183         /**
12184          * The second of the minute. Ranges from 0 to 59.
12185          * @type number
12186          */
12187         this.second = parseInt(params.second, 10) || 0;
12188         /**
12189          * The millisecond of the second. Ranges from 0 to 999.
12190          * @type number
12191          */
12192         this.millisecond = parseInt(params.millisecond, 10) || 0;
12193 
12194         /**
12195          * The day of the year. Ranges from 1 to 383.
12196          * @type number
12197          */
12198         this.dayOfYear = parseInt(params.dayOfYear, 10);
12199 
12200         if (typeof(params.dst) === 'boolean') {
12201             this.dst = params.dst;
12202         }
12203 
12204         this.rd = this.newRd(this);
12205 
12206         new TimeZone({
12207             id: this.timezone,
12208             sync: params.sync,
12209             loadParams: params.loadParams,
12210             onLoad: ilib.bind(this, function(tz) {
12211                 this.tz = tz;
12212                 // add the time zone offset to the rd to convert to UTC
12213                 // getOffsetMillis requires that this.year, this.rd, and this.dst 
12214                 // are set in order to figure out which time zone rules apply and 
12215                 // what the offset is at that point in the year
12216                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
12217                 if (this.offset !== 0) {
12218                     this.rd = this.newRd({
12219                         rd: this.rd.getRataDie() - this.offset
12220                     });
12221                 }
12222                 this._init2(params);
12223             })
12224         });
12225     } else {
12226         this._init2(params);
12227     }
12228 };
12229 
12230 /**
12231  * @private
12232  * Finish initializing the date
12233  */
12234 JulianDate.prototype._init2 = function (params) {
12235     if (!this.rd) {
12236         this.rd = this.newRd(params);
12237         this._calcDateComponents();
12238     }
12239 
12240     if (typeof(params.onLoad) === "function") {
12241         params.onLoad(this);
12242     }
12243 };
12244 
12245 /**
12246  * Return a new RD for this date type using the given params.
12247  * @protected
12248  * @param {Object=} params the parameters used to create this rata die instance
12249  * @returns {RataDie} the new RD instance for the given params
12250  */
12251 JulianDate.prototype.newRd = function (params) {
12252 	return new JulianRataDie(params);
12253 };
12254 
12255 /**
12256  * Return the year for the given RD
12257  * @protected
12258  * @param {number} rd RD to calculate from 
12259  * @returns {number} the year for the RD
12260  */
12261 JulianDate.prototype._calcYear = function(rd) {
12262 	var year = Math.floor((4*(Math.floor(rd)-1) + 1464)/1461);
12263 	
12264 	return (year <= 0) ? year - 1 : year;
12265 };
12266 
12267 /**
12268  * Calculate date components for the given RD date.
12269  * @protected
12270  */
12271 JulianDate.prototype._calcDateComponents = function () {
12272 	var remainder,
12273 		cumulative,
12274 		rd = this.rd.getRataDie();
12275 	
12276 	this.year = this._calcYear(rd);
12277 
12278 	if (typeof(this.offset) === "undefined") {
12279 		this.year = this._calcYear(rd);
12280 		
12281 		// now offset the RD by the time zone, then recalculate in case we were 
12282 		// near the year boundary
12283 		if (!this.tz) {
12284 			this.tz = new TimeZone({id: this.timezone});
12285 		}
12286 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
12287 	}
12288 
12289 	if (this.offset !== 0) {
12290 		rd += this.offset;
12291 		this.year = this._calcYear(rd);
12292 	}
12293 	
12294 	var jan1 = this.newRd({
12295 		year: this.year,
12296 		month: 1,
12297 		day: 1,
12298 		hour: 0,
12299 		minute: 0,
12300 		second: 0,
12301 		millisecond: 0
12302 	});
12303 	remainder = rd + 1 - jan1.getRataDie();
12304 	
12305 	cumulative = this.cal.isLeapYear(this.year) ? 
12306 		JulianCal.cumMonthLengthsLeap : 
12307 		JulianCal.cumMonthLengths; 
12308 	
12309 	this.month = SearchUtils.bsearch(Math.floor(remainder), cumulative);
12310 	remainder = remainder - cumulative[this.month-1];
12311 	
12312 	this.day = Math.floor(remainder);
12313 	remainder -= this.day;
12314 	// now convert to milliseconds for the rest of the calculation
12315 	remainder = Math.round(remainder * 86400000);
12316 	
12317 	this.hour = Math.floor(remainder/3600000);
12318 	remainder -= this.hour * 3600000;
12319 	
12320 	this.minute = Math.floor(remainder/60000);
12321 	remainder -= this.minute * 60000;
12322 	
12323 	this.second = Math.floor(remainder/1000);
12324 	remainder -= this.second * 1000;
12325 	
12326 	this.millisecond = remainder;
12327 };
12328 
12329 /**
12330  * Return the day of the week of this date. The day of the week is encoded
12331  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
12332  * 
12333  * @return {number} the day of the week
12334  */
12335 JulianDate.prototype.getDayOfWeek = function() {
12336 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
12337 	return MathUtils.mod(rd-2, 7);
12338 };
12339 
12340 /**
12341  * Return the name of the calendar that governs this date.
12342  * 
12343  * @return {string} a string giving the name of the calendar
12344  */
12345 JulianDate.prototype.getCalendar = function() {
12346 	return "julian";
12347 };
12348 
12349 //register with the factory method
12350 IDate._constructors["julian"] = JulianDate;
12351 
12352 
12353 /*< GregorianDate.js */
12354 /*
12355  * GregorianDate.js - Represent a date in the Gregorian calendar
12356  *
12357  * Copyright © 2012-2015, 2018, JEDLSoft
12358  *
12359  * Licensed under the Apache License, Version 2.0 (the "License");
12360  * you may not use this file except in compliance with the License.
12361  * You may obtain a copy of the License at
12362  *
12363  *     http://www.apache.org/licenses/LICENSE-2.0
12364  *
12365  * Unless required by applicable law or agreed to in writing, software
12366  * distributed under the License is distributed on an "AS IS" BASIS,
12367  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12368  *
12369  * See the License for the specific language governing permissions and
12370  * limitations under the License.
12371  */
12372 
12373 /* !depends 
12374 ilib.js
12375 IDate.js 
12376 GregorianCal.js 
12377 SearchUtils.js
12378 MathUtils.js
12379 Locale.js
12380 LocaleInfo.js 
12381 GregRataDie.js
12382 TimeZone.js
12383 */
12384 
12385 
12386 
12387 
12388 /**
12389  * @class
12390  * Construct a new Gregorian date object. The constructor parameters can
12391  * contain any of the following properties:
12392  *
12393  * <ul>
12394  * <li><i>unixtime<i> - sets the time of this instance according to the given
12395  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
12396  *
12397  * <li><i>julianday</i> - sets the time of this instance according to the given
12398  * Julian Day instance or the Julian Day given as a float
12399  *
12400  * <li><i>year</i> - any integer, including 0
12401  *
12402  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12403  *
12404  * <li><i>day</i> - 1 to 31
12405  *
12406  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
12407  * is always done with an unambiguous 24 hour representation
12408  *
12409  * <li><i>minute</i> - 0 to 59
12410  *
12411  * <li><i>second</i> - 0 to 59
12412  *
12413  * <li><i>millisecond</i> - 0 to 999
12414  *
12415  * <li><i>dst</i> - boolean used to specify whether the given time components are
12416  * intended to be in daylight time or not. This is only used in the overlap
12417  * time when transitioning from DST to standard time, and the time components are
12418  * ambiguous. Otherwise at all other times of the year, this flag is ignored.
12419  * If you specify the date using unix time (UTC) or a julian day, then the time is
12420  * already unambiguous and this flag does not need to be specified.
12421  * <p>
12422  * For example, in the US, the transition out of daylight savings time
12423  * in 2014 happens at Nov 2, 2014 2:00am Daylight Time, when the time falls
12424  * back to Nov 2, 2014 1:00am Standard Time. If you give a date/time components as
12425  * "Nov 2, 2014 1:30am", then there are two 1:30am times in that day, and you would
12426  * have to give the standard flag to indicate which of those two you mean.
12427  * (dst=true means daylight time, dst=false means standard time).
12428  *
12429  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string
12430  * of this gregorian date. The date/time is kept in the local time. The time zone
12431  * is used later if this date is formatted according to a different time zone and
12432  * the difference has to be calculated, or when the date format has a time zone
12433  * component in it.
12434  *
12435  * <li><i>locale</i> - locale for this gregorian date. If the time zone is not
12436  * given, it can be inferred from this locale. For locales that span multiple
12437  * time zones, the one with the largest population is chosen as the one that
12438  * represents the locale.
12439  *
12440  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
12441  *
12442  * <li><i>onLoad</i> - a callback function to call when this date object is fully
12443  * loaded. When the onLoad option is given, this date object will attempt to
12444  * load any missing locale data using the ilib loader callback.
12445  * When the constructor is done (even if the data is already preassembled), the
12446  * onLoad function is called with the current instance as a parameter, so this
12447  * callback can be used with preassembled or dynamic loading or a mix of the two.
12448  *
12449  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
12450  * asynchronously. If this option is given as "false", then the "onLoad"
12451  * callback must be given, as the instance returned from this constructor will
12452  * not be usable for a while.
12453  *
12454  * <li><i>loadParams</i> - an object containing parameters to pass to the
12455  * loader callback function when locale data is missing. The parameters are not
12456  * interpretted or modified in any way. They are simply passed along. The object
12457  * may contain any property/value pairs as long as the calling code is in
12458  * agreement with the loader callback function as to what those parameters mean.
12459  * </ul>
12460  *
12461  * If the constructor is called with another Gregorian date instance instead of
12462  * a parameter block, the other instance acts as a parameter block and its
12463  * settings are copied into the current instance.<p>
12464  *
12465  * If the constructor is called with no arguments at all or if none of the
12466  * properties listed above
12467  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date
12468  * components are
12469  * filled in with the current date at the time of instantiation. Note that if
12470  * you do not give the time zone when defaulting to the current time and the
12471  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
12472  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich
12473  * Mean Time").<p>
12474  *
12475  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
12476  * specified in the params, it is assumed that they have the smallest possible
12477  * value in the range for the property (zero or one).<p>
12478  *
12479  *
12480  * @constructor
12481  * @extends IDate
12482  * @param {Object=} params parameters that govern the settings and behaviour of this Gregorian date
12483  */
12484 var GregorianDate = function(params) {
12485     this.cal = new GregorianCal();
12486 
12487     params = params || {};
12488     if (typeof(params.noinstance) === 'boolean' && params.noinstance) {
12489         // for doing inheritance, so don't need to fill in the data. The
12490         // inheriting class only wants the methods.
12491         return;
12492     }
12493 
12494     if (params.timezone) {
12495         this.timezone = params.timezone.toString();
12496     }
12497     if (params.locale) {
12498         this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
12499     }
12500 
12501     if (!this.timezone) {
12502         if (this.locale) {
12503             new LocaleInfo(this.locale, {
12504                 sync: params.sync,
12505                 loadParams: params.loadParams,
12506                 onLoad: ilib.bind(this, function(li) {
12507                     this.li = li;
12508                     this.timezone = li.getTimeZone();
12509                     this._init(params);
12510                 })
12511             });
12512         } else {
12513             this.timezone = "local";
12514             this._init(params);
12515         }
12516     } else {
12517         this._init(params);
12518     }
12519 };
12520 
12521 GregorianDate.prototype = new IDate({noinstance: true});
12522 GregorianDate.prototype.parent = IDate;
12523 GregorianDate.prototype.constructor = GregorianDate;
12524 
12525 /**
12526  * @private
12527  * Initialize this date object
12528  */
12529 GregorianDate.prototype._init = function (params) {
12530     if (params.year || params.month || params.day || params.hour ||
12531         params.minute || params.second || params.millisecond ) {
12532         this.year = parseInt(params.year, 10) || 0;
12533         this.month = parseInt(params.month, 10) || 1;
12534         this.day = parseInt(params.day, 10) || 1;
12535         this.hour = parseInt(params.hour, 10) || 0;
12536         this.minute = parseInt(params.minute, 10) || 0;
12537         this.second = parseInt(params.second, 10) || 0;
12538         this.millisecond = parseInt(params.millisecond, 10) || 0;
12539         if (typeof(params.dst) === 'boolean') {
12540             this.dst = params.dst;
12541         }
12542         this.rd = this.newRd(params);
12543 
12544         // add the time zone offset to the rd to convert to UTC
12545         this.offset = 0;
12546         if (this.timezone === "local" && typeof(params.dst) === 'undefined') {
12547             // if dst is defined, the intrinsic Date object has no way of specifying which version of a time you mean
12548             // in the overlap time at the end of DST. Do you mean the daylight 1:30am or the standard 1:30am? In this
12549             // case, use the ilib calculations below, which can distinguish between the two properly
12550             var d = new Date(this.year, this.month-1, this.day, this.hour, this.minute, this.second, this.millisecond);
12551             var hBefore = new Date(this.year, this.month-1, this.day, this.hour - 1, this.minute, this.second, this.millisecond);
12552             this.offset = -d.getTimezoneOffset() / 1440;
12553             if (d.getTimezoneOffset() < hBefore.getTimezoneOffset()) {
12554                 var startOffset = -hBefore.getTimezoneOffset() / 1440;
12555                 this.rd = this.newRd({
12556                     rd: this.rd.getRataDie() - startOffset
12557                 });
12558             } else {
12559                 this.rd = this.newRd({
12560                     rd: this.rd.getRataDie() - this.offset
12561                 });
12562             }
12563             this._init2(params);
12564         } else {
12565             new TimeZone({
12566                 id: this.timezone,
12567                 sync: params.sync,
12568                 loadParams: params.loadParams,
12569                 onLoad: ilib.bind(this, function(tz) {
12570                     this.tz = tz;
12571 
12572                     // getOffsetMillis requires that this.year, this.rd, and this.dst
12573                     // are set in order to figure out which time zone rules apply and
12574                     // what the offset is at that point in the year
12575                     this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
12576                     this.rd = this.newRd({
12577                         rd: this.rd.getRataDie() - this.offset
12578                     });
12579                     this._init2(params);
12580                 })
12581             });
12582         }
12583     } else {
12584         this._init2(params);
12585     }
12586 };
12587 
12588 /**
12589  * @private
12590  * Finish initializing this date object
12591  */
12592 GregorianDate.prototype._init2 = function (params) {
12593     if (!this.rd) {
12594         this.rd = this.newRd(params);
12595         this._calcDateComponents();
12596     }
12597 
12598     if (typeof(params.onLoad) === "function") {
12599         params.onLoad(this);
12600     }
12601 };
12602 
12603 /**
12604  * Return a new RD for this date type using the given params.
12605  * @private
12606  * @param {Object=} params the parameters used to create this rata die instance
12607  * @returns {RataDie} the new RD instance for the given params
12608  */
12609 GregorianDate.prototype.newRd = function (params) {
12610 	return new GregRataDie(params);
12611 };
12612 
12613 /**
12614  * Calculates the Gregorian year for a given rd number.
12615  * @private
12616  * @static
12617  */
12618 GregorianDate._calcYear = function(rd) {
12619 	var days400,
12620 		days100,
12621 		days4,
12622 		years400,
12623 		years100,
12624 		years4,
12625 		years1,
12626 		year;
12627 
12628 	years400 = Math.floor((rd - 1) / 146097);
12629 	days400 = MathUtils.mod((rd - 1), 146097);
12630 	years100 = Math.floor(days400 / 36524);
12631 	days100 = MathUtils.mod(days400, 36524);
12632 	years4 = Math.floor(days100 / 1461);
12633 	days4 = MathUtils.mod(days100, 1461);
12634 	years1 = Math.floor(days4 / 365);
12635 
12636 	year = 400 * years400 + 100 * years100 + 4 * years4 + years1;
12637 	if (years100 !== 4 && years1 !== 4) {
12638 		year++;
12639 	}
12640 	return year;
12641 };
12642 
12643 /**
12644  * @private
12645  */
12646 GregorianDate.prototype._calcYear = function(rd) {
12647 	return GregorianDate._calcYear(rd);
12648 };
12649 
12650 /**
12651  * Calculate the date components for the current time zone
12652  * @private
12653  */
12654 GregorianDate.prototype._calcDateComponents = function () {
12655 	if (this.timezone === "local" && this.rd.getRataDie() >= -99280837 && this.rd.getRataDie() <= 100719163) {
12656 		// console.log("using js Date to calculate offset");
12657 		// use the intrinsic JS Date object to do the tz conversion for us, which
12658 		// guarantees that it follows the system tz database settings
12659 		var d = new Date(this.rd.getTimeExtended());
12660 
12661 		/**
12662 		 * Year in the Gregorian calendar.
12663 		 * @type number
12664 		 */
12665 		this.year = d.getFullYear();
12666 
12667 		/**
12668 		 * The month number, ranging from 1 (January) to 12 (December).
12669 		 * @type number
12670 		 */
12671 		this.month = d.getMonth()+1;
12672 
12673 		/**
12674 		 * The day of the month. This ranges from 1 to 31.
12675 		 * @type number
12676 		 */
12677 		this.day = d.getDate();
12678 
12679 		/**
12680 		 * The hour of the day. This can be a number from 0 to 23, as times are
12681 		 * stored unambiguously in the 24-hour clock.
12682 		 * @type number
12683 		 */
12684 		this.hour = d.getHours();
12685 
12686 		/**
12687 		 * The minute of the hours. Ranges from 0 to 59.
12688 		 * @type number
12689 		 */
12690 		this.minute = d.getMinutes();
12691 
12692 		/**
12693 		 * The second of the minute. Ranges from 0 to 59.
12694 		 * @type number
12695 		 */
12696 		this.second = d.getSeconds();
12697 
12698 		/**
12699 		 * The millisecond of the second. Ranges from 0 to 999.
12700 		 * @type number
12701 		 */
12702 		this.millisecond = d.getMilliseconds();
12703 
12704 		this.offset = -d.getTimezoneOffset() / 1440;
12705 	} else {
12706 		// console.log("using ilib to calculate offset. tz is " + this.timezone);
12707 		// console.log("GregDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
12708 		if (typeof(this.offset) === "undefined") {
12709 			// console.log("calculating offset");
12710 			this.year = this._calcYear(this.rd.getRataDie());
12711 
12712 			// now offset the RD by the time zone, then recalculate in case we were
12713 			// near the year boundary
12714 			if (!this.tz) {
12715 				this.tz = new TimeZone({id: this.timezone});
12716 			}
12717 			this.offset = this.tz.getOffsetMillis(this) / 86400000;
12718 		// } else {
12719 			// console.log("offset is already defined somehow. type is " + typeof(this.offset));
12720 			// console.trace("Stack is this one");
12721 		}
12722 		// console.log("offset is " + this.offset);
12723 		var rd = this.rd.getRataDie();
12724 		if (this.offset !== 0) {
12725 			rd += this.offset;
12726 		}
12727 		this.year = this._calcYear(rd);
12728 
12729 		var yearStartRd = this.newRd({
12730 			year: this.year,
12731 			month: 1,
12732 			day: 1,
12733 			cal: this.cal
12734 		});
12735 
12736 		// remainder is days into the year
12737 		var remainder = rd - yearStartRd.getRataDie() + 1;
12738 
12739 		var cumulative = GregorianCal.prototype.isLeapYear.call(this.cal, this.year) ?
12740 			GregRataDie.cumMonthLengthsLeap :
12741 			GregRataDie.cumMonthLengths;
12742 
12743 		this.month = SearchUtils.bsearch(Math.floor(remainder), cumulative);
12744 		remainder = remainder - cumulative[this.month-1];
12745 
12746 		this.day = Math.floor(remainder);
12747 		remainder -= this.day;
12748 		// now convert to milliseconds for the rest of the calculation
12749 		remainder = Math.round(remainder * 86400000);
12750 
12751 		this.hour = Math.floor(remainder/3600000);
12752 		remainder -= this.hour * 3600000;
12753 
12754 		this.minute = Math.floor(remainder/60000);
12755 		remainder -= this.minute * 60000;
12756 
12757 		this.second = Math.floor(remainder/1000);
12758 		remainder -= this.second * 1000;
12759 
12760 		this.millisecond = Math.floor(remainder);
12761 	}
12762 };
12763 
12764 /**
12765  * Return the day of the week of this date. The day of the week is encoded
12766  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
12767  *
12768  * @return {number} the day of the week
12769  */
12770 GregorianDate.prototype.getDayOfWeek = function() {
12771 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
12772 	return MathUtils.mod(rd, 7);
12773 };
12774 
12775 /**
12776  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to
12777  * 365, regardless of months or weeks, etc. That is, January 1st is day 1, and
12778  * December 31st is 365 in regular years, or 366 in leap years.
12779  * @return {number} the ordinal day of the year
12780  */
12781 GregorianDate.prototype.getDayOfYear = function() {
12782 	var cumulativeMap = this.cal.isLeapYear(this.year) ?
12783 		GregRataDie.cumMonthLengthsLeap :
12784 		GregRataDie.cumMonthLengths;
12785 
12786 	return cumulativeMap[this.month-1] + this.day;
12787 };
12788 
12789 /**
12790  * Return the era for this date as a number. The value for the era for Gregorian
12791  * calendars is -1 for "before the common era" (BCE) and 1 for "the common era" (CE).
12792  * BCE dates are any date before Jan 1, 1 CE. In the proleptic Gregorian calendar,
12793  * there is a year 0, so any years that are negative or zero are BCE. In the Julian
12794  * calendar, there is no year 0. Instead, the calendar goes straight from year -1 to
12795  * 1.
12796  * @return {number} 1 if this date is in the common era, -1 if it is before the
12797  * common era
12798  */
12799 GregorianDate.prototype.getEra = function() {
12800 	return (this.year < 1) ? -1 : 1;
12801 };
12802 
12803 /**
12804  * Return the name of the calendar that governs this date.
12805  *
12806  * @return {string} a string giving the name of the calendar
12807  */
12808 GregorianDate.prototype.getCalendar = function() {
12809 	return "gregorian";
12810 };
12811 
12812 // register with the factory method
12813 IDate._constructors["gregorian"] = GregorianDate;
12814 
12815 
12816 
12817 /*< ThaiSolarCal.js */
12818 /*
12819  * ThaiSolarCal.js - Represent a Thai solar calendar object.
12820  *
12821  * Copyright © 2013-2015,2018, JEDLSoft
12822  *
12823  * Licensed under the Apache License, Version 2.0 (the "License");
12824  * you may not use this file except in compliance with the License.
12825  * You may obtain a copy of the License at
12826  *
12827  *     http://www.apache.org/licenses/LICENSE-2.0
12828  *
12829  * Unless required by applicable law or agreed to in writing, software
12830  * distributed under the License is distributed on an "AS IS" BASIS,
12831  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12832  *
12833  * See the License for the specific language governing permissions and
12834  * limitations under the License.
12835  */
12836 
12837 
12838 /* !depends Calendar.js GregorianCal.js MathUtils.js */
12839 
12840 
12841 /**
12842  * @class
12843  * Construct a new Thai solar calendar object. This class encodes information about
12844  * a Thai solar calendar.<p>
12845  *
12846  * @param {Object=} options Options governing the construction of this instance
12847  * @constructor
12848  * @extends Calendar
12849  */
12850 var ThaiSolarCal = function(options) {
12851 	this.type = "thaisolar";
12852     
12853     if (options && typeof(options.onLoad) === "function") {
12854         options.onLoad(this);
12855     }
12856 };
12857 
12858 ThaiSolarCal.prototype = new GregorianCal({noinstance: true});
12859 ThaiSolarCal.prototype.parent = GregorianCal;
12860 ThaiSolarCal.prototype.constructor = ThaiSolarCal;
12861 
12862 /**
12863  * Return true if the given year is a leap year in the Thai solar calendar.
12864  * The year parameter may be given as a number, or as a ThaiSolarDate object.
12865  * @param {number|ThaiSolarDate} year the year for which the leap year information is being sought
12866  * @return {boolean} true if the given year is a leap year
12867  */
12868 ThaiSolarCal.prototype.isLeapYear = function(year) {
12869 	var y = (typeof(year) === 'number' ? year : year.getYears());
12870 	y -= 543;
12871 	var centuries = MathUtils.mod(y, 400);
12872 	return (MathUtils.mod(y, 4) === 0 && centuries !== 100 && centuries !== 200 && centuries !== 300);
12873 };
12874 
12875 
12876 /* register this calendar for the factory method */
12877 Calendar._constructors["thaisolar"] = ThaiSolarCal;
12878 
12879 
12880 
12881 /*< ThaiSolarDate.js */
12882 /*
12883  * ThaiSolarDate.js - Represent a date in the ThaiSolar calendar
12884  * 
12885  * Copyright © 2013-2015, JEDLSoft
12886  *
12887  * Licensed under the Apache License, Version 2.0 (the "License");
12888  * you may not use this file except in compliance with the License.
12889  * You may obtain a copy of the License at
12890  *
12891  *     http://www.apache.org/licenses/LICENSE-2.0
12892  *
12893  * Unless required by applicable law or agreed to in writing, software
12894  * distributed under the License is distributed on an "AS IS" BASIS,
12895  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12896  *
12897  * See the License for the specific language governing permissions and
12898  * limitations under the License.
12899  */
12900 
12901 /* !depends 
12902 ilib.js
12903 IDate.js 
12904 JSUtils.js
12905 GregorianDate.js
12906 ThaiSolarCal.js
12907 */
12908 
12909 
12910 
12911 
12912 /**
12913  * @class
12914  * Construct a new Thai solar date object. The constructor parameters can 
12915  * contain any of the following properties:
12916  * 
12917  * <ul>
12918  * <li><i>unixtime<i> - sets the time of this instance according to the given 
12919  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
12920  * 
12921  * <li><i>julianday</i> - sets the time of this instance according to the given
12922  * Julian Day instance or the Julian Day given as a float
12923  * 
12924  * <li><i>year</i> - any integer, including 0
12925  * 
12926  * <li><i>month</i> - 1 to 12, where 1 means January, 2 means February, etc.
12927  * 
12928  * <li><i>day</i> - 1 to 31
12929  * 
12930  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
12931  * is always done with an unambiguous 24 hour representation
12932  * 
12933  * <li><i>minute</i> - 0 to 59
12934  * 
12935  * <li><i>second</i> - 0 to 59
12936  * 
12937  * <li><i>millisecond</i> - 0 to 999
12938  * 
12939  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
12940  * of this Thai solar date. The date/time is kept in the local time. The time zone
12941  * is used later if this date is formatted according to a different time zone and
12942  * the difference has to be calculated, or when the date format has a time zone
12943  * component in it.
12944  * 
12945  * <li><i>locale</i> - locale for this Thai solar date. If the time zone is not 
12946  * given, it can be inferred from this locale. For locales that span multiple
12947  * time zones, the one with the largest population is chosen as the one that 
12948  * represents the locale. 
12949  * </ul>
12950  *
12951  * If the constructor is called with another Thai solar date instance instead of
12952  * a parameter block, the other instance acts as a parameter block and its
12953  * settings are copied into the current instance.<p>
12954  * 
12955  * If the constructor is called with no arguments at all or if none of the 
12956  * properties listed above 
12957  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
12958  * components are 
12959  * filled in with the current date at the time of instantiation. Note that if
12960  * you do not give the time zone when defaulting to the current time and the 
12961  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
12962  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
12963  * Mean Time").<p>
12964  * 
12965  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
12966  * specified in the params, it is assumed that they have the smallest possible
12967  * value in the range for the property (zero or one).<p>
12968  * 
12969  * 
12970  * @constructor
12971  * @extends GregorianDate
12972  * @param {Object=} params parameters that govern the settings and behaviour of this Thai solar date
12973  */
12974 var ThaiSolarDate = function(params) {
12975     var p = {};
12976 
12977     if (params) {
12978         JSUtils.shallowCopy(params, p);
12979         
12980         // there is 198327 days difference between the Thai solar and
12981         // Gregorian epochs which is equivalent to 543 years
12982         if (typeof(p.year) !== 'undefined') {
12983             p.year -= 543;
12984         }
12985         if (typeof(p.rd) !== 'undefined') {
12986             p.rd -= 198327;
12987         }
12988     }
12989     this.rd = null; // clear these out so that the GregorianDate constructor can set it
12990     this.offset = undefined;
12991     //console.log("ThaiSolarDate.constructor: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
12992 
12993     p.onLoad = ilib.bind(this, function(gd) {
12994         this.cal = new ThaiSolarCal();
12995         
12996         // make sure the year is set correctly from the original params
12997         if (params && typeof(params.year) !== 'undefined') {
12998             this.year = parseInt(params.year, 10);
12999         }
13000 
13001         if (params && typeof(params.onLoad) === "function") {
13002             params.onLoad(gd);
13003         }
13004     });
13005 
13006     GregorianDate.call(this, p);
13007 };
13008 
13009 ThaiSolarDate.prototype = new GregorianDate({noinstance: true});
13010 ThaiSolarDate.prototype.parent = GregorianDate.prototype;
13011 ThaiSolarDate.prototype.constructor = ThaiSolarDate;
13012 
13013 /**
13014  * the difference between a zero Julian day and the zero Thai Solar date.
13015  * This is some 543 years before the start of the Gregorian epoch. 
13016  * @private
13017  * @type number
13018  */
13019 ThaiSolarDate.epoch = 1523097.5;
13020 
13021 /**
13022  * Calculate the date components for the current time zone
13023  * @protected
13024  */
13025 ThaiSolarDate.prototype._calcDateComponents = function () {
13026 	// there is 198327 days difference between the Thai solar and 
13027 	// Gregorian epochs which is equivalent to 543 years
13028 	// console.log("ThaiSolarDate._calcDateComponents: date is " + JSON.stringify(this) + " parent is " + JSON.stringify(this.parent) + " and parent.parent is " + JSON.stringify(this.parent.parent));
13029 	this.parent._calcDateComponents.call(this);
13030 	this.year += 543;
13031 };
13032 
13033 /**
13034  * Return the Rata Die (fixed day) number of this date.
13035  * 
13036  * @protected
13037  * @return {number} the rd date as a number
13038  */
13039 ThaiSolarDate.prototype.getRataDie = function() {
13040 	// there is 198327 days difference between the Thai solar and 
13041 	// Gregorian epochs which is equivalent to 543 years
13042 	return this.rd.getRataDie() + 198327;
13043 };
13044 
13045 /**
13046  * Return a new Gregorian date instance that represents the first instance of the 
13047  * given day of the week before the current date. The day of the week is encoded
13048  * as a number where 0 = Sunday, 1 = Monday, etc.
13049  * 
13050  * @param {number} dow the day of the week before the current date that is being sought
13051  * @return {IDate} the date being sought
13052  */
13053 ThaiSolarDate.prototype.before = function (dow) {
13054 	return new ThaiSolarDate({
13055 		rd: this.rd.before(dow, this.offset) + 198327,
13056 		timezone: this.timezone
13057 	});
13058 };
13059 
13060 /**
13061  * Return a new Gregorian date instance that represents the first instance of the 
13062  * given day of the week after the current date. The day of the week is encoded
13063  * as a number where 0 = Sunday, 1 = Monday, etc.
13064  * 
13065  * @param {number} dow the day of the week after the current date that is being sought
13066  * @return {IDate} the date being sought
13067  */
13068 ThaiSolarDate.prototype.after = function (dow) {
13069 	return new ThaiSolarDate({
13070 		rd: this.rd.after(dow, this.offset) + 198327,
13071 		timezone: this.timezone
13072 	});
13073 };
13074 
13075 /**
13076  * Return a new Gregorian date instance that represents the first instance of the 
13077  * given day of the week on or before the current date. The day of the week is encoded
13078  * as a number where 0 = Sunday, 1 = Monday, etc.
13079  * 
13080  * @param {number} dow the day of the week on or before the current date that is being sought
13081  * @return {IDate} the date being sought
13082  */
13083 ThaiSolarDate.prototype.onOrBefore = function (dow) {
13084 	return new ThaiSolarDate({
13085 		rd: this.rd.onOrBefore(dow, this.offset) + 198327,
13086 		timezone: this.timezone
13087 	});
13088 };
13089 
13090 /**
13091  * Return a new Gregorian date instance that represents the first instance of the 
13092  * given day of the week on or after the current date. The day of the week is encoded
13093  * as a number where 0 = Sunday, 1 = Monday, etc.
13094  * 
13095  * @param {number} dow the day of the week on or after the current date that is being sought
13096  * @return {IDate} the date being sought
13097  */
13098 ThaiSolarDate.prototype.onOrAfter = function (dow) {
13099 	return new ThaiSolarDate({
13100 		rd: this.rd.onOrAfter(dow, this.offset) + 198327,
13101 		timezone: this.timezone
13102 	});
13103 };
13104 
13105 /**
13106  * Return the name of the calendar that governs this date.
13107  * 
13108  * @return {string} a string giving the name of the calendar
13109  */
13110 ThaiSolarDate.prototype.getCalendar = function() {
13111 	return "thaisolar";
13112 };
13113 
13114 //register with the factory method
13115 IDate._constructors["thaisolar"] = ThaiSolarDate;
13116 
13117 
13118 
13119 /*< Astro.js */
13120 /*
13121  * astro.js - Static functions to support astronomical calculations
13122  * 
13123  * Copyright © 2014-2015, JEDLSoft
13124  *
13125  * Licensed under the Apache License, Version 2.0 (the "License");
13126  * you may not use this file except in compliance with the License.
13127  * You may obtain a copy of the License at
13128  *
13129  *     http://www.apache.org/licenses/LICENSE-2.0
13130  *
13131  * Unless required by applicable law or agreed to in writing, software
13132  * distributed under the License is distributed on an "AS IS" BASIS,
13133  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13134  *
13135  * See the License for the specific language governing permissions and
13136  * limitations under the License.
13137  */
13138 
13139 /* !depends
13140 ilib.js
13141 IDate.js
13142 Utils.js
13143 SearchUtils.js
13144 GregorianDate.js
13145 GregRataDie.js
13146 */
13147 
13148 // !data astro
13149 
13150 /*
13151  * These routines were derived from a public domain set of JavaScript 
13152  * functions for positional astronomy by John Walker of Fourmilab, 
13153  * September 1999.
13154  */
13155 
13156 
13157 
13158 var Astro = {};
13159 
13160 /**
13161  * Load in all the data needed for astrological calculations.
13162  * 
13163  * @private
13164  * @param {boolean} sync
13165  * @param {*} loadParams
13166  * @param {function(*)|undefined} callback
13167  */
13168 Astro.initAstro = function(sync, loadParams, callback) {
13169 	if (!ilib.data.astro) {
13170 		Utils.loadData({
13171 			object: "Astro",
13172 			name: "astro.json", // countries in their own language 
13173 			locale: "-", // only need to load the root file 
13174 			nonLocale: true,
13175 			sync: sync, 
13176 			loadParams: loadParams, 
13177 			callback: ilib.bind(this, function(astroData) {
13178 				/** 
13179 				 * @type {{
13180 				 *  	_EquinoxpTerms:Array.<number>, 
13181 				 *  	_JDE0tab1000:Array.<number>, 
13182 				 *  	_JDE0tab2000:Array.<number>, 
13183 				 *  	_deltaTtab:Array.<number>,
13184 				 *  	_oterms:Array.<number>,
13185 				 *  	_nutArgMult:Array.<number>, 
13186 				 *  	_nutArgCoeff:Array.<number>, 
13187 				 *  	_nutCoeffA:Array.<number>,
13188 				 *  	_nutCoeffB:Array.<number>,
13189 				 *  	_coeff19th:Array.<number>,
13190 				 *  	_coeff18th:Array.<number>,
13191 				 *  	_solarLongCoeff:Array.<number>, 
13192 				 *  	_solarLongMultipliers:Array.<number>, 
13193 				 *  	_solarLongAddends:Array.<number>, 
13194 				 *  	_meanMoonCoeff:Array.<number>,
13195 				 *  	_elongationCoeff:Array.<number>,
13196 				 *  	_solarAnomalyCoeff:Array.<number>,
13197 				 *  	_lunarAnomalyCoeff:Array.<number>,
13198 				 *  	_moonFromNodeCoeff:Array.<number>,
13199 				 *  	_eCoeff:Array.<number>,
13200 				 *  	_lunarElongationLongCoeff:Array.<number>,
13201 				 *  	_solarAnomalyLongCoeff:Array.<number>,
13202 				 *  	_lunarAnomalyLongCoeff:Array.<number>,
13203 				 *  	_moonFromNodeLongCoeff:Array.<number>,
13204 				 *  	_sineCoeff:Array.<number>,
13205 				 *  	_nmApproxCoeff:Array.<number>,
13206 				 *  	_nmCapECoeff:Array.<number>,
13207 				 *  	_nmSolarAnomalyCoeff:Array.<number>,
13208 				 *  	_nmLunarAnomalyCoeff:Array.<number>,
13209 				 *  	_nmMoonArgumentCoeff:Array.<number>,
13210 				 *  	_nmCapOmegaCoeff:Array.<number>,
13211 				 *  	_nmEFactor:Array.<number>,
13212 				 *  	_nmSolarCoeff:Array.<number>,
13213 				 *  	_nmLunarCoeff:Array.<number>,
13214 				 *  	_nmMoonCoeff:Array.<number>,
13215 				 *  	_nmSineCoeff:Array.<number>,
13216 				 *  	_nmAddConst:Array.<number>,
13217 				 *  	_nmAddCoeff:Array.<number>,
13218 				 *  	_nmAddFactor:Array.<number>,
13219 				 *  	_nmExtra:Array.<number>
13220 				 *  }}
13221 				 */ 	
13222 			 	ilib.data.astro = astroData;
13223 				if (callback && typeof(callback) === 'function') {
13224 					callback(astroData);
13225 				}
13226 			})
13227 		});
13228 	} else {
13229 		if (callback && typeof(callback) === 'function') {
13230 			callback(ilib.data.astro);
13231 		}
13232 	}
13233 };
13234 
13235 /**
13236  * Convert degrees to radians.
13237  * 
13238  * @static
13239  * @protected
13240  * @param {number} d angle in degrees
13241  * @return {number} angle in radians 
13242  */
13243 Astro._dtr = function(d) {
13244 	return (d * Math.PI) / 180.0;
13245 };
13246 
13247 /**
13248  * Convert radians to degrees.
13249  * 
13250  * @static
13251  * @protected
13252  * @param {number} r angle in radians
13253  * @return {number} angle in degrees 
13254  */
13255 Astro._rtd = function(r) {
13256 	return (r * 180.0) / Math.PI;
13257 };
13258 
13259 /**
13260  * Return the cosine of an angle given in degrees.
13261  * @static
13262  * @protected
13263  * @param {number} d angle in degrees
13264  * @return {number} cosine of the angle.
13265  */  
13266 Astro._dcos = function(d) {
13267 	return Math.cos(Astro._dtr(d));
13268 };
13269 
13270 /**
13271  * Return the sine of an angle given in degrees.
13272  * @static
13273  * @protected
13274  * @param {number} d angle in degrees
13275  * @return {number} sine of the angle.
13276  */  
13277 Astro._dsin = function(d) {
13278 	return Math.sin(Astro._dtr(d));
13279 };
13280 
13281 /**
13282  * Return the tan of an angle given in degrees.
13283  * @static
13284  * @protected
13285  * @param {number} d angle in degrees
13286  * @return {number} tan of the angle.
13287  */  
13288 Astro._dtan = function(d) {
13289 	return Math.tan(Astro._dtr(d));
13290 };
13291 
13292 /**
13293  * Range reduce angle in degrees.
13294  * 
13295  * @static
13296  * @param {number} a angle to reduce
13297  * @return {number} the reduced angle  
13298  */
13299 Astro._fixangle = function(a) {
13300 	return a - 360.0 * (Math.floor(a / 360.0));
13301 };
13302 
13303 /**
13304  * Range reduce angle in radians.
13305  * 
13306  * @static
13307  * @protected
13308  * @param {number} a angle to reduce
13309  * @return {number} the reduced angle  
13310  */
13311 Astro._fixangr = function(a) {
13312 	return a - (2 * Math.PI) * (Math.floor(a / (2 * Math.PI)));
13313 };
13314 
13315 /**
13316  * Determine the Julian Ephemeris Day of an equinox or solstice.  The "which" 
13317  * argument selects the item to be computed:
13318  * 
13319  * <ul>
13320  * <li>0   March equinox
13321  * <li>1   June solstice
13322  * <li>2   September equinox
13323  * <li>3   December solstice
13324  * </ul>
13325  * 
13326  * @static
13327  * @protected
13328  * @param {number} year Gregorian year to calculate for
13329  * @param {number} which Which equinox or solstice to calculate
13330  */
13331 Astro._equinox = function(year, which) {
13332 	var deltaL, i, j, JDE0, JDE, JDE0tab, S, T, W, Y;
13333 
13334 	/*  Initialize terms for mean equinox and solstices.  We
13335 	    have two sets: one for years prior to 1000 and a second
13336 	    for subsequent years.  */
13337 
13338 	if (year < 1000) {
13339 		JDE0tab = ilib.data.astro._JDE0tab1000;
13340 		Y = year / 1000;
13341 	} else {
13342 		JDE0tab = ilib.data.astro._JDE0tab2000;
13343 		Y = (year - 2000) / 1000;
13344 	}
13345 
13346 	JDE0 = JDE0tab[which][0] + (JDE0tab[which][1] * Y)
13347 			+ (JDE0tab[which][2] * Y * Y) + (JDE0tab[which][3] * Y * Y * Y)
13348 			+ (JDE0tab[which][4] * Y * Y * Y * Y);
13349 
13350 	//document.debug.log.value += "JDE0 = " + JDE0 + "\n";
13351 
13352 	T = (JDE0 - 2451545.0) / 36525;
13353 	//document.debug.log.value += "T = " + T + "\n";
13354 	W = (35999.373 * T) - 2.47;
13355 	//document.debug.log.value += "W = " + W + "\n";
13356 	deltaL = 1 + (0.0334 * Astro._dcos(W)) + (0.0007 * Astro._dcos(2 * W));
13357 	//document.debug.log.value += "deltaL = " + deltaL + "\n";
13358 
13359 	//  Sum the periodic terms for time T
13360 
13361 	S = 0;
13362 	j = 0;
13363 	for (i = 0; i < 24; i++) {
13364 		S += ilib.data.astro._EquinoxpTerms[j]
13365 				* Astro._dcos(ilib.data.astro._EquinoxpTerms[j + 1] + (ilib.data.astro._EquinoxpTerms[j + 2] * T));
13366 		j += 3;
13367 	}
13368 
13369 	//document.debug.log.value += "S = " + S + "\n";
13370 	//document.debug.log.value += "Corr = " + ((S * 0.00001) / deltaL) + "\n";
13371 
13372 	JDE = JDE0 + ((S * 0.00001) / deltaL);
13373 
13374 	return JDE;
13375 };
13376 
13377 /* 
13378  * The table of observed Delta T values at the beginning of
13379  * years from 1620 through 2014 as found in astro.json is taken from
13380  * http://www.staff.science.uu.nl/~gent0113/deltat/deltat.htm
13381  * and
13382  * ftp://maia.usno.navy.mil/ser7/deltat.data
13383  */
13384 
13385 /**  
13386  * Determine the difference, in seconds, between dynamical time and universal time.
13387  * 
13388  * @static
13389  * @protected
13390  * @param {number} year to calculate the difference for
13391  * @return {number} difference in seconds between dynamical time and universal time  
13392  */
13393 Astro._deltat = function (year) {
13394 	var dt, f, i, t;
13395 
13396 	if ((year >= 1620) && (year <= 2014)) {
13397 		i = Math.floor(year - 1620);
13398 		f = (year - 1620) - i; /* Fractional part of year */
13399 		dt = ilib.data.astro._deltaTtab[i] + ((ilib.data.astro._deltaTtab[i + 1] - ilib.data.astro._deltaTtab[i]) * f);
13400 	} else {
13401 		t = (year - 2000) / 100;
13402 		if (year < 948) {
13403 			dt = 2177 + (497 * t) + (44.1 * t * t);
13404 		} else {
13405 			dt = 102 + (102 * t) + (25.3 * t * t);
13406 			if ((year > 2000) && (year < 2100)) {
13407 				dt += 0.37 * (year - 2100);
13408 			}
13409 		}
13410 	}
13411 	return dt;
13412 };
13413 
13414 /**
13415  * Calculate the obliquity of the ecliptic for a given
13416  * Julian date.  This uses Laskar's tenth-degree
13417  * polynomial fit (J. Laskar, Astronomy and
13418  * Astrophysics, Vol. 157, page 68 [1986]) which is
13419  * accurate to within 0.01 arc second between AD 1000
13420  * and AD 3000, and within a few seconds of arc for
13421  * +/-10000 years around AD 2000.  If we're outside the
13422  * range in which this fit is valid (deep time) we
13423  * simply return the J2000 value of the obliquity, which
13424  * happens to be almost precisely the mean.
13425  * 
13426  * @static
13427  * @protected
13428  * @param {number} jd Julian Day to calculate the obliquity for
13429  * @return {number} the obliquity
13430  */
13431 Astro._obliqeq = function (jd) {
13432 	var eps, u, v, i;
13433 
13434  	v = u = (jd - 2451545.0) / 3652500.0;
13435 
13436  	eps = 23 + (26 / 60.0) + (21.448 / 3600.0);
13437 
13438  	if (Math.abs(u) < 1.0) {
13439  		for (i = 0; i < 10; i++) {
13440  			eps += (ilib.data.astro._oterms[i] / 3600.0) * v;
13441  			v *= u;
13442  		}
13443  	}
13444  	return eps;
13445 };
13446 
13447 /**
13448  * Return the position of the sun.  We return
13449  * intermediate values because they are useful in a
13450  * variety of other contexts.
13451  * @static
13452  * @protected
13453  * @param {number} jd find the position of sun on this Julian Day
13454  * @return {Object} the position of the sun and many intermediate
13455  * values
13456  */
13457 Astro._sunpos = function(jd) {
13458 	var ret = {}, 
13459 		T, T2, T3, Omega, epsilon, epsilon0;
13460 
13461 	T = (jd - 2451545.0) / 36525.0;
13462 	//document.debug.log.value += "Sunpos.  T = " + T + "\n";
13463 	T2 = T * T;
13464 	T3 = T * T2;
13465 	ret.meanLongitude = Astro._fixangle(280.46646 + 36000.76983 * T + 0.0003032 * T2);
13466 	//document.debug.log.value += "ret.meanLongitude = " + ret.meanLongitude + "\n";
13467 	ret.meanAnomaly = Astro._fixangle(357.52911 + (35999.05029 * T) - 0.0001537 * T2 - 0.00000048 * T3);
13468 	//document.debug.log.value += "ret.meanAnomaly = " + ret.meanAnomaly + "\n";
13469 	ret.eccentricity = 0.016708634 - 0.000042037 * T - 0.0000001267 * T2;
13470 	//document.debug.log.value += "e = " + e + "\n";
13471 	ret.equationOfCenter = ((1.914602 - 0.004817 * T - 0.000014 * T2) * Astro._dsin(ret.meanAnomaly))
13472 			+ ((0.019993 - 0.000101 * T) * Astro._dsin(2 * ret.meanAnomaly))
13473 			+ (0.000289 * Astro._dsin(3 * ret.meanAnomaly));
13474 	//document.debug.log.value += "ret.equationOfCenter = " + ret.equationOfCenter + "\n";
13475 	ret.sunLongitude = ret.meanLongitude + ret.equationOfCenter;
13476 	//document.debug.log.value += "ret.sunLongitude = " + ret.sunLongitude + "\n";
13477 	//ret.sunAnomaly = ret.meanAnomaly + ret.equationOfCenter;
13478 	//document.debug.log.value += "ret.sunAnomaly = " + ret.sunAnomaly + "\n";
13479 	// ret.sunRadius = (1.000001018 * (1 - (ret.eccentricity * ret.eccentricity))) / (1 + (ret.eccentricity * Astro._dcos(ret.sunAnomaly)));
13480 	//document.debug.log.value += "ret.sunRadius = " + ret.sunRadius + "\n";
13481 	Omega = 125.04 - (1934.136 * T);
13482 	//document.debug.log.value += "Omega = " + Omega + "\n";
13483 	ret.apparentLong = ret.sunLongitude + (-0.00569) + (-0.00478 * Astro._dsin(Omega));
13484 	//document.debug.log.value += "ret.apparentLong = " + ret.apparentLong + "\n";
13485 	epsilon0 = Astro._obliqeq(jd);
13486 	//document.debug.log.value += "epsilon0 = " + epsilon0 + "\n";
13487 	epsilon = epsilon0 + (0.00256 * Astro._dcos(Omega));
13488 	//document.debug.log.value += "epsilon = " + epsilon + "\n";
13489 	//ret.rightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon0) * Astro._dsin(ret.sunLongitude), Astro._dcos(ret.sunLongitude))));
13490 	//document.debug.log.value += "ret.rightAscension = " + ret.rightAscension + "\n";
13491 	// ret.declination = Astro._rtd(Math.asin(Astro._dsin(epsilon0) * Astro._dsin(ret.sunLongitude)));
13492 	////document.debug.log.value += "ret.declination = " + ret.declination + "\n";
13493 	ret.inclination = Astro._fixangle(23.4392911 - 0.013004167 * T - 0.00000016389 * T2 + 0.0000005036 * T3);
13494 	ret.apparentRightAscension = Astro._fixangle(Astro._rtd(Math.atan2(Astro._dcos(epsilon) * Astro._dsin(ret.apparentLong), Astro._dcos(ret.apparentLong))));
13495 	//document.debug.log.value += "ret.apparentRightAscension = " + ret.apparentRightAscension + "\n";
13496 	//ret.apparentDeclination = Astro._rtd(Math.asin(Astro._dsin(epsilon) * Astro._dsin(ret.apparentLong)));
13497 	//document.debug.log.value += "ret.apparentDecliation = " + ret.apparentDecliation + "\n";
13498 
13499 	// Angular quantities are expressed in decimal degrees
13500 	return ret;
13501 };
13502 
13503 /**
13504  * Calculate the nutation in longitude, deltaPsi, and obliquity, 
13505  * deltaEpsilon for a given Julian date jd. Results are returned as an object
13506  * giving deltaPsi and deltaEpsilon in degrees.
13507  * 
13508  * @static
13509  * @protected
13510  * @param {number} jd calculate the nutation of this Julian Day
13511  * @return {Object} the deltaPsi and deltaEpsilon of the nutation
13512  */
13513 Astro._nutation = function(jd) {
13514 	var i, j, 
13515 		t = (jd - 2451545.0) / 36525.0, 
13516 		t2, t3, to10, 
13517 		ta = [], 
13518 		dp = 0, 
13519 		de = 0, 
13520 		ang,
13521 		ret = {};
13522 
13523 	t3 = t * (t2 = t * t);
13524 
13525 	/*
13526 	 * Calculate angles. The correspondence between the elements of our array
13527 	 * and the terms cited in Meeus are:
13528 	 * 
13529 	 * ta[0] = D ta[0] = M ta[2] = M' ta[3] = F ta[4] = \Omega
13530 	 * 
13531 	 */
13532 
13533 	ta[0] = Astro._dtr(297.850363 + 445267.11148 * t - 0.0019142 * t2 + t3 / 189474.0);
13534 	ta[1] = Astro._dtr(357.52772 + 35999.05034 * t - 0.0001603 * t2 - t3 / 300000.0);
13535 	ta[2] = Astro._dtr(134.96298 + 477198.867398 * t + 0.0086972 * t2 + t3 / 56250.0);
13536 	ta[3] = Astro._dtr(93.27191 + 483202.017538 * t - 0.0036825 * t2 + t3 / 327270);
13537 	ta[4] = Astro._dtr(125.04452 - 1934.136261 * t + 0.0020708 * t2 + t3 / 450000.0);
13538 
13539 	/*
13540 	 * Range reduce the angles in case the sine and cosine functions don't do it
13541 	 * as accurately or quickly.
13542 	 */
13543 
13544 	for (i = 0; i < 5; i++) {
13545 		ta[i] = Astro._fixangr(ta[i]);
13546 	}
13547 
13548 	to10 = t / 10.0;
13549 	for (i = 0; i < 63; i++) {
13550 		ang = 0;
13551 		for (j = 0; j < 5; j++) {
13552 			if (ilib.data.astro._nutArgMult[(i * 5) + j] != 0) {
13553 				ang += ilib.data.astro._nutArgMult[(i * 5) + j] * ta[j];
13554 			}
13555 		}
13556 		dp += (ilib.data.astro._nutArgCoeff[(i * 4) + 0] + ilib.data.astro._nutArgCoeff[(i * 4) + 1] * to10) * Math.sin(ang);
13557 		de += (ilib.data.astro._nutArgCoeff[(i * 4) + 2] + ilib.data.astro._nutArgCoeff[(i * 4) + 3] * to10) * Math.cos(ang);
13558 	}
13559 
13560 	/*
13561 	 * Return the result, converting from ten thousandths of arc seconds to
13562 	 * radians in the process.
13563 	 */
13564 
13565 	ret.deltaPsi = dp / (3600.0 * 10000.0);
13566 	ret.deltaEpsilon = de / (3600.0 * 10000.0);
13567 
13568 	return ret;
13569 };
13570 
13571 /**
13572  * Returns the equation of time as a fraction of a day.
13573  * 
13574  * @static
13575  * @protected
13576  * @param {number} jd the Julian Day of the day to calculate for
13577  * @return {number} the equation of time for the given day  
13578  */
13579 Astro._equationOfTime = function(jd) {
13580 	var alpha, deltaPsi, E, epsilon, L0, tau, pos;
13581 
13582 	// 2451545.0 is the Julian day of J2000 epoch
13583 	// 365250.0 is the number of days in a Julian millenium
13584 	tau = (jd - 2451545.0) / 365250.0;
13585 	//console.log("equationOfTime.  tau = " + tau);
13586 	L0 = 280.4664567 + (360007.6982779 * tau) + (0.03032028 * tau * tau)
13587 			+ ((tau * tau * tau) / 49931)
13588 			+ (-((tau * tau * tau * tau) / 15300))
13589 			+ (-((tau * tau * tau * tau * tau) / 2000000));
13590 	//console.log("L0 = " + L0);
13591 	L0 = Astro._fixangle(L0);
13592 	//console.log("L0 = " + L0);
13593 	pos = Astro._sunpos(jd);
13594 	alpha = pos.apparentRightAscension;
13595 	//console.log("alpha = " + alpha);
13596 	var nut = Astro._nutation(jd);
13597 	deltaPsi = nut.deltaPsi;
13598 	//console.log("deltaPsi = " + deltaPsi);
13599 	epsilon = Astro._obliqeq(jd) + nut.deltaEpsilon;
13600 	//console.log("epsilon = " + epsilon);
13601 	//console.log("L0 - 0.0057183 = " + (L0 - 0.0057183));
13602 	//console.log("L0 - 0.0057183 - alpha = " + (L0 - 0.0057183 - alpha));
13603 	//console.log("deltaPsi * cos(epsilon) = " + deltaPsi * Astro._dcos(epsilon));
13604 	
13605 	E = L0 - 0.0057183 - alpha + deltaPsi * Astro._dcos(epsilon);
13606 	// if alpha and L0 are in different quadrants, then renormalize
13607 	// so that the difference between them is in the right range
13608 	if (E > 180) {
13609 		E -= 360;
13610 	}
13611 	//console.log("E = " + E);
13612 	// E = E - 20.0 * (Math.floor(E / 20.0));
13613 	E = E * 4;
13614 	//console.log("Efixed = " + E);
13615 	E = E / (24 * 60);
13616 	//console.log("Eday = " + E);
13617 
13618 	return E;
13619 };
13620 
13621 /**
13622  * @private
13623  * @static
13624  */
13625 Astro._poly = function(x, coefficients) {
13626 	var result = coefficients[0];
13627 	var xpow = x;
13628 	for (var i = 1; i < coefficients.length; i++) {
13629 		result += coefficients[i] * xpow;
13630 		xpow *= x;
13631 	}
13632 	return result;
13633 };
13634 
13635 /**
13636  * Calculate the UTC RD from the local RD given "zone" number of minutes
13637  * worth of offset.
13638  * 
13639  * @static
13640  * @protected
13641  * @param {number} local RD of the locale time, given in any calendar
13642  * @param {number} zone number of minutes of offset from UTC for the time zone 
13643  * @return {number} the UTC equivalent of the local RD
13644  */
13645 Astro._universalFromLocal = function(local, zone) {
13646 	return local - zone / 1440;
13647 };
13648 
13649 /**
13650  * Calculate the local RD from the UTC RD given "zone" number of minutes
13651  * worth of offset.
13652  * 
13653  * @static
13654  * @protected
13655  * @param {number} local RD of the locale time, given in any calendar
13656  * @param {number} zone number of minutes of offset from UTC for the time zone 
13657  * @return {number} the UTC equivalent of the local RD
13658  */
13659 Astro._localFromUniversal = function(local, zone) {
13660 	return local + zone / 1440;
13661 };
13662 
13663 /**
13664  * @private
13665  * @static
13666  * @param {number} c julian centuries of the date to calculate
13667  * @return {number} the aberration
13668  */
13669 Astro._aberration = function(c) {
13670 	return 9.74e-05 * Astro._dcos(177.63 + 35999.01847999999 * c) - 0.005575;
13671 };
13672 
13673 /**
13674  * @private
13675  *
13676 ilib.data.astro._nutCoeffA = [124.90, -1934.134, 0.002063];
13677 ilib.data.astro._nutCoeffB q= [201.11, 72001.5377, 0.00057];
13678 */
13679 
13680 /**
13681  * @private
13682  * @static
13683  * @param {number} c julian centuries of the date to calculate
13684  * @return {number} the nutation for the given julian century in radians
13685  */
13686 Astro._nutation2 = function(c) {
13687 	var a = Astro._poly(c, ilib.data.astro._nutCoeffA);
13688 	var b = Astro._poly(c, ilib.data.astro._nutCoeffB);
13689 	// return -0.0000834 * Astro._dsin(a) - 0.0000064 * Astro._dsin(b);
13690 	return -0.004778 * Astro._dsin(a) - 0.0003667 * Astro._dsin(b);
13691 };
13692 
13693 /**
13694  * @static
13695  * @private
13696  */
13697 Astro._ephemerisCorrection = function(jd) {
13698 	var year = GregorianDate._calcYear(jd - 1721424.5);
13699 	
13700 	if (1988 <= year && year <= 2019) {
13701 		return (year - 1933) / 86400;
13702 	}
13703 	
13704 	if (1800 <= year && year <= 1987) {
13705 		var jul1 = new GregRataDie({
13706 			year: year,
13707 			month: 7,
13708 			day: 1,
13709 			hour: 0,
13710 			minute: 0,
13711 			second: 0
13712 		});
13713 		// 693596 is the rd of Jan 1, 1900
13714 		var theta = (jul1.getRataDie() - 693596) / 36525;
13715 		return Astro._poly(theta, (1900 <= year) ? ilib.data.astro._coeff19th : ilib.data.astro._coeff18th);
13716 	}
13717 	
13718 	if (1620 <= year && year <= 1799) {
13719 		year -= 1600;
13720 		return (196.58333 - 4.0675 * year + 0.0219167 * year * year) / 86400;
13721 	}
13722 	
13723 	// 660724 is the rd of Jan 1, 1810
13724 	var jan1 = new GregRataDie({
13725 		year: year,
13726 		month: 1,
13727 		day: 1,
13728 		hour: 0,
13729 		minute: 0,
13730 		second: 0
13731 	});
13732 	// var x = 0.5 + (jan1.getRataDie() - 660724);
13733 	var x = 0.5 + (jan1.getRataDie() - 660724);
13734 	
13735 	return ((x * x / 41048480) - 15) / 86400;
13736 };
13737 
13738 /**
13739  * @static
13740  * @private
13741  */
13742 Astro._ephemerisFromUniversal = function(jd) {
13743 	return jd + Astro._ephemerisCorrection(jd);
13744 };
13745 
13746 /**
13747  * @static
13748  * @private
13749  */
13750 Astro._universalFromEphemeris = function(jd) {
13751 	return jd - Astro._ephemerisCorrection(jd);
13752 };
13753 
13754 /**
13755  * @static
13756  * @private
13757  */
13758 Astro._julianCenturies = function(jd) {
13759 	// 2451545.0 is the Julian day of J2000 epoch
13760 	// 730119.5 is the Gregorian RD of J2000 epoch
13761 	// 36525.0 is the number of days in a Julian century
13762 	return (Astro._ephemerisFromUniversal(jd) - 2451545.0) / 36525.0;
13763 };
13764 
13765 /**
13766  * Calculate the solar longitude
13767  * 
13768  * @static
13769  * @protected
13770  * @param {number} jd julian day of the date to calculate the longitude for 
13771  * @return {number} the solar longitude in degrees
13772  */
13773 Astro._solarLongitude = function(jd) {
13774 	var c = Astro._julianCenturies(jd),
13775 		longitude = 0,
13776 		len = ilib.data.astro._solarLongCoeff.length;
13777 	
13778 	for (var i = 0; i < len; i++) {
13779 		longitude += ilib.data.astro._solarLongCoeff[i] * 
13780 			Astro._dsin(ilib.data.astro._solarLongAddends[i] + ilib.data.astro._solarLongMultipliers[i] * c);
13781 	}
13782 	longitude *= 5.729577951308232e-06;
13783 	longitude += 282.77718340000001 + 36000.769537439999 * c;
13784 	longitude += Astro._aberration(c) + Astro._nutation2(c);
13785 	return Astro._fixangle(longitude);
13786 };
13787 
13788 /**
13789  * @static
13790  * @protected
13791  * @param {number} jd
13792  * @return {number}
13793  */
13794 Astro._lunarLongitude = function (jd) {
13795 	var c = Astro._julianCenturies(jd),
13796 	    meanMoon = Astro._fixangle(Astro._poly(c, ilib.data.astro._meanMoonCoeff)),
13797 	    elongation = Astro._fixangle(Astro._poly(c, ilib.data.astro._elongationCoeff)),
13798 	    solarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._solarAnomalyCoeff)),
13799 	    lunarAnomaly = Astro._fixangle(Astro._poly(c, ilib.data.astro._lunarAnomalyCoeff)),
13800 	    moonNode = Astro._fixangle(Astro._poly(c, ilib.data.astro._moonFromNodeCoeff)),
13801 	    e = Astro._poly(c, ilib.data.astro._eCoeff);
13802 	
13803 	var sum = 0;
13804 	for (var i = 0; i < ilib.data.astro._lunarElongationLongCoeff.length; i++) {
13805 		var x = ilib.data.astro._solarAnomalyLongCoeff[i];
13806 
13807 		sum += ilib.data.astro._sineCoeff[i] * Math.pow(e, Math.abs(x)) * 
13808 			Astro._dsin(ilib.data.astro._lunarElongationLongCoeff[i] * elongation + x * solarAnomaly + 
13809 				ilib.data.astro._lunarAnomalyLongCoeff[i] * lunarAnomaly + 
13810 				ilib.data.astro._moonFromNodeLongCoeff[i] * moonNode);
13811 	}
13812 	var longitude = sum / 1000000;
13813 	var venus = 3958.0 / 1000000 * Astro._dsin(119.75 + c * 131.84899999999999);
13814 	var jupiter = 318.0 / 1000000 * Astro._dsin(53.090000000000003 + c * 479264.28999999998);
13815 	var flatEarth = 1962.0 / 1000000 * Astro._dsin(meanMoon - moonNode);
13816 	
13817 	return Astro._fixangle(meanMoon + longitude + venus + jupiter + flatEarth + Astro._nutation2(c));
13818 };
13819 
13820 /**
13821  * @static
13822  * @protected
13823  * @param {number} n
13824  * @return {number} julian day of the n'th new moon
13825  */
13826 Astro._newMoonTime = function(n) {
13827 	var k = n - 24724;
13828 	var c = k / 1236.8499999999999;
13829 	var approx = Astro._poly(c, ilib.data.astro._nmApproxCoeff);
13830 	var capE = Astro._poly(c, ilib.data.astro._nmCapECoeff);
13831 	var solarAnomaly = Astro._poly(c, ilib.data.astro._nmSolarAnomalyCoeff);
13832 	var lunarAnomaly = Astro._poly(c, ilib.data.astro._nmLunarAnomalyCoeff);
13833 	var moonArgument = Astro._poly(c, ilib.data.astro._nmMoonArgumentCoeff);
13834 	var capOmega = Astro._poly(c, ilib.data.astro._nmCapOmegaCoeff);
13835 	var correction = -0.00017 * Astro._dsin(capOmega);
13836 	for (var i = 0; i < ilib.data.astro._nmSineCoeff.length; i++) {
13837 		correction = correction + ilib.data.astro._nmSineCoeff[i] * Math.pow(capE, ilib.data.astro._nmEFactor[i]) * 
13838 		Astro._dsin(ilib.data.astro._nmSolarCoeff[i] * solarAnomaly + 
13839 				ilib.data.astro._nmLunarCoeff[i] * lunarAnomaly + 
13840 				ilib.data.astro._nmMoonCoeff[i] * moonArgument);
13841 	}
13842 	var additional = 0;
13843 	for (var i = 0; i < ilib.data.astro._nmAddConst.length; i++) {
13844 		additional = additional + ilib.data.astro._nmAddFactor[i] * 
13845 		Astro._dsin(ilib.data.astro._nmAddConst[i] + ilib.data.astro._nmAddCoeff[i] * k);
13846 	}
13847 	var extra = 0.000325 * Astro._dsin(Astro._poly(c, ilib.data.astro._nmExtra));
13848 	return Astro._universalFromEphemeris(approx + correction + extra + additional + RataDie.gregorianEpoch);
13849 };
13850 
13851 /**
13852  * @static
13853  * @protected
13854  * @param {number} jd
13855  * @return {number}
13856  */
13857 Astro._lunarSolarAngle = function(jd) {
13858 	var lunar = Astro._lunarLongitude(jd);
13859 	var solar = Astro._solarLongitude(jd)
13860 	return Astro._fixangle(lunar - solar);
13861 };
13862 
13863 /**
13864  * @static
13865  * @protected
13866  * @param {number} jd
13867  * @return {number}
13868  */
13869 Astro._newMoonBefore = function (jd) {
13870 	var phase = Astro._lunarSolarAngle(jd);
13871 	// 11.450086114414322 is the julian day of the 0th full moon
13872 	// 29.530588853000001 is the average length of a month
13873 	var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360) - 1;
13874 	var current, last;
13875 	current = last = Astro._newMoonTime(guess);
13876 	while (current < jd) {
13877 		guess++;
13878 		last = current;
13879 		current = Astro._newMoonTime(guess);
13880 	}
13881 	return last;
13882 };
13883 
13884 /**
13885  * @static
13886  * @protected
13887  * @param {number} jd
13888  * @return {number}
13889  */
13890 Astro._newMoonAtOrAfter = function (jd) {
13891 	var phase = Astro._lunarSolarAngle(jd);
13892 	// 11.450086114414322 is the julian day of the 0th full moon
13893 	// 29.530588853000001 is the average length of a month
13894 	var guess = Math.round((jd - 11.450086114414322 - RataDie.gregorianEpoch) / 29.530588853000001 - phase / 360);
13895 	var current;
13896 	while ((current = Astro._newMoonTime(guess)) < jd) {
13897 		guess++;
13898 	}
13899 	return current;
13900 };
13901 
13902 /**
13903  * @static
13904  * @protected
13905  * @param {number} jd JD to calculate from
13906  * @param {number} longitude longitude to seek 
13907  * @returns {number} the JD of the next time that the solar longitude 
13908  * is a multiple of the given longitude
13909  */
13910 Astro._nextSolarLongitude = function(jd, longitude) {
13911 	var rate = 365.242189 / 360.0;
13912 	var tau = jd + rate * Astro._fixangle(longitude - Astro._solarLongitude(jd));
13913 	var start = Math.max(jd, tau - 5.0);
13914 	var end = tau + 5.0;
13915 	
13916 	return SearchUtils.bisectionSearch(0, start, end, 1e-6, function (l) {
13917 		return 180 - Astro._fixangle(Astro._solarLongitude(l) - longitude);
13918 	});
13919 };
13920 
13921 /**
13922  * Floor the julian day to midnight of the current julian day.
13923  * 
13924  * @static
13925  * @protected
13926  * @param {number} jd the julian to round
13927  * @return {number} the jd floored to the midnight of the julian day
13928  */
13929 Astro._floorToJD = function(jd) {
13930 	return Math.floor(jd - 0.5) + 0.5;
13931 };
13932 
13933 /**
13934  * Floor the julian day to midnight of the current julian day.
13935  * 
13936  * @static
13937  * @protected
13938  * @param {number} jd the julian to round
13939  * @return {number} the jd floored to the midnight of the julian day
13940  */
13941 Astro._ceilToJD = function(jd) {
13942 	return Math.ceil(jd + 0.5) - 0.5;
13943 };
13944 
13945 
13946 
13947 /*< PersRataDie.js */
13948 /*
13949  * persratadie.js - Represent a rata die date in the Persian calendar
13950  *
13951  * Copyright © 2014-2015, JEDLSoft
13952  *
13953  * Licensed under the Apache License, Version 2.0 (the "License");
13954  * you may not use this file except in compliance with the License.
13955  * You may obtain a copy of the License at
13956  *
13957  *     http://www.apache.org/licenses/LICENSE-2.0
13958  *
13959  * Unless required by applicable law or agreed to in writing, software
13960  * distributed under the License is distributed on an "AS IS" BASIS,
13961  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13962  *
13963  * See the License for the specific language governing permissions and
13964  * limitations under the License.
13965  */
13966 
13967 /* !depends
13968 ilib.js
13969 MathUtils.js
13970 RataDie.js
13971 Astro.js
13972 GregorianDate.js
13973 */
13974 
13975 
13976 
13977 
13978 /**
13979  * @class
13980  * Construct a new Persian RD date number object. The constructor parameters can
13981  * contain any of the following properties:
13982  *
13983  * <ul>
13984  * <li><i>unixtime<i> - sets the time of this instance according to the given
13985  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
13986  *
13987  * <li><i>julianday</i> - sets the time of this instance according to the given
13988  * Julian Day instance or the Julian Day given as a float
13989  *
13990  * <li><i>year</i> - any integer, including 0
13991  *
13992  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
13993  *
13994  * <li><i>day</i> - 1 to 31
13995  *
13996  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
13997  * is always done with an unambiguous 24 hour representation
13998  *
13999  * <li><i>minute</i> - 0 to 59
14000  *
14001  * <li><i>second</i> - 0 to 59
14002  *
14003  * <li><i>millisecond</i> - 0 to 999
14004  *
14005  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
14006  * </ul>
14007  *
14008  * If the constructor is called with another Persian date instance instead of
14009  * a parameter block, the other instance acts as a parameter block and its
14010  * settings are copied into the current instance.<p>
14011  *
14012  * If the constructor is called with no arguments at all or if none of the
14013  * properties listed above are present, then the RD is calculate based on
14014  * the current date at the time of instantiation. <p>
14015  *
14016  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
14017  * specified in the params, it is assumed that they have the smallest possible
14018  * value in the range for the property (zero or one).<p>
14019  *
14020  *
14021  * @private
14022  * @constructor
14023  * @extends RataDie
14024  * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date
14025  */
14026 var PersRataDie = function(params) {
14027 	this.rd = NaN;
14028 	Astro.initAstro(
14029 		params && typeof(params.sync) === 'boolean' ? params.sync : true,
14030 		params && params.loadParams,
14031 		ilib.bind(this, function (x) {
14032 		    RataDie.call(this, params);
14033 			if (params && typeof(params.callback) === 'function') {
14034 				params.callback(this);
14035 			}
14036 		})
14037 	);
14038 };
14039 
14040 PersRataDie.prototype = new RataDie();
14041 PersRataDie.prototype.parent = RataDie;
14042 PersRataDie.prototype.constructor = PersRataDie;
14043 
14044 /**
14045  * The difference between a zero Julian day and the first Persian date
14046  * @private
14047  * @type number
14048  */
14049 PersRataDie.prototype.epoch = 1948319.5;
14050 
14051 /**
14052  * @protected
14053  */
14054 PersRataDie.prototype._tehranEquinox = function(year) {
14055     var equJED, equJD, equAPP, equTehran, dtTehran, eot;
14056 
14057     //  March equinox in dynamical time
14058     equJED = Astro._equinox(year, 0);
14059 
14060     //  Correct for delta T to obtain Universal time
14061     equJD = equJED - (Astro._deltat(year) / (24 * 60 * 60));
14062 
14063     //  Apply the equation of time to yield the apparent time at Greenwich
14064     eot = Astro._equationOfTime(equJED) * 360;
14065     eot = (eot - 20 * Math.floor(eot/20)) / 360;
14066     equAPP = equJD + eot;
14067 
14068     /*
14069      * Finally, we must correct for the constant difference between
14070      * the Greenwich meridian and the time zone standard for Iran
14071      * Standard time, 52 degrees 30 minutes to the East.
14072      */
14073 
14074     dtTehran = 52.5 / 360;
14075     equTehran = equAPP + dtTehran;
14076 
14077     return equTehran;
14078 };
14079 
14080 /**
14081  * Calculate the year based on the given Julian day.
14082  * @protected
14083  * @param {number} jd the Julian day to get the year for
14084  * @return {{year:number,equinox:number}} the year and the last equinox
14085  */
14086 PersRataDie.prototype._getYear = function(jd) {
14087 	var gd = new GregorianDate({julianday: jd});
14088     var guess = gd.getYears() - 2,
14089     	nexteq,
14090     	ret = {};
14091 
14092     //ret.equinox = Math.floor(this._tehranEquinox(guess));
14093     ret.equinox = this._tehranEquinox(guess);
14094 	while (ret.equinox > jd) {
14095 	    guess--;
14096 	    // ret.equinox = Math.floor(this._tehranEquinox(guess));
14097 	    ret.equinox = this._tehranEquinox(guess);
14098 	}
14099 	nexteq = ret.equinox - 1;
14100 	// if the equinox falls after noon, then the day after that is the start of the
14101 	// next year, so truncate the JD to get the noon of the day before the day with
14102 	//the equinox on it, then add 0.5 to get the midnight of that day
14103 	while (!(Math.floor(ret.equinox) + 0.5 <= jd && jd < Math.floor(nexteq) + 0.5)) {
14104 	    ret.equinox = nexteq;
14105 	    guess++;
14106 	    // nexteq = Math.floor(this._tehranEquinox(guess));
14107 	    nexteq = this._tehranEquinox(guess);
14108 	}
14109 
14110 	// Mean solar tropical year is 365.24219878 days
14111 	ret.year = Math.round((ret.equinox - this.epoch - 1) / 365.24219878) + 1;
14112 
14113 	return ret;
14114 };
14115 
14116 /**
14117  * Calculate the Rata Die (fixed day) number of the given date from the
14118  * date components.
14119  *
14120  * @protected
14121  * @param {Object} date the date components to calculate the RD from
14122  */
14123 PersRataDie.prototype._setDateComponents = function(date) {
14124     var adr, guess, jd;
14125 
14126     // Mean solar tropical year is 365.24219878 days
14127     guess = this.epoch + 1 + 365.24219878 * ((date.year || 0) - 2);
14128     adr = {year: (date.year || 0) - 1, equinox: 0};
14129 
14130     while (adr.year < date.year) {
14131         adr = this._getYear(guess);
14132         guess = adr.equinox + (365.24219878 + 2);
14133     }
14134 
14135     jd = Math.floor(adr.equinox) +
14136             (((date.month || 0) <= 7) ?
14137                 (((date.month || 1) - 1) * 31) :
14138                 ((((date.month || 1) - 1) * 30) + 6)
14139             ) +
14140     	    ((date.day || 1) - 1 + 0.5); // add 0.5 so that we convert JDs, which start at noon to RDs which start at midnight
14141 
14142 	jd += ((date.hour || 0) * 3600000 +
14143 			(date.minute || 0) * 60000 +
14144 			(date.second || 0) * 1000 +
14145 			(date.millisecond || 0)) /
14146 			86400000;
14147 
14148     this.rd = jd - this.epoch;
14149 };
14150 
14151 /**
14152  * Return the rd number of the particular day of the week on or before the
14153  * given rd. eg. The Sunday on or before the given rd.
14154  * @private
14155  * @param {number} rd the rata die date of the reference date
14156  * @param {number} dayOfWeek the day of the week that is being sought relative
14157  * to the current date
14158  * @return {number} the rd of the day of the week
14159  */
14160 PersRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
14161 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 3, 7);
14162 };
14163 
14164 
14165 /*< PersianCal.js */
14166 /*
14167  * PersianCal.js - Represent a Persian astronomical (Hijjri) calendar object.
14168  * 
14169  * Copyright © 2014-2015,2018, JEDLSoft
14170  *
14171  * Licensed under the Apache License, Version 2.0 (the "License");
14172  * you may not use this file except in compliance with the License.
14173  * You may obtain a copy of the License at
14174  *
14175  *     http://www.apache.org/licenses/LICENSE-2.0
14176  *
14177  * Unless required by applicable law or agreed to in writing, software
14178  * distributed under the License is distributed on an "AS IS" BASIS,
14179  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14180  *
14181  * See the License for the specific language governing permissions and
14182  * limitations under the License.
14183  */
14184 
14185 
14186 /* !depends 
14187 Calendar.js 
14188 PersRataDie.js 
14189 */
14190 
14191 
14192 
14193 /**
14194  * @class
14195  * Construct a new Persian astronomical (Hijjri) calendar object. This class encodes 
14196  * information about a Persian calendar. This class differs from the 
14197  * Persian calendar in that the leap years are calculated based on the
14198  * astronomical observations of the sun in Teheran, instead of calculating
14199  * the leap years based on a regular cyclical rhythm algorithm.<p>
14200  * 
14201  * @param {Object=} options Options governing the construction of this instance
14202  * @constructor
14203  * @extends Calendar
14204  */
14205 var PersianCal = function(options) {
14206 	this.type = "persian";
14207     
14208     if (options && typeof(options.onLoad) === "function") {
14209         options.onLoad(this);
14210     }
14211 };
14212 
14213 /**
14214  * @private
14215  * @const
14216  * @type Array.<number> 
14217  * the lengths of each month 
14218  */
14219 PersianCal.monthLengths = [
14220 	31,  // Farvardin
14221 	31,  // Ordibehesht
14222 	31,  // Khordad
14223 	31,  // Tir
14224 	31,  // Mordad
14225 	31,  // Shahrivar
14226 	30,  // Mehr
14227 	30,  // Aban
14228 	30,  // Azar
14229 	30,  // Dey
14230 	30,  // Bahman
14231 	29   // Esfand
14232 ];
14233 
14234 /**
14235  * Return the number of months in the given year. The number of months in a year varies
14236  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
14237  * days in a year to an entire solar year. The month is represented as a 1-based number
14238  * where 1=first month, 2=second month, etc.
14239  * 
14240  * @param {number} year a year for which the number of months is sought
14241  * @return {number} The number of months in the given year
14242  */
14243 PersianCal.prototype.getNumMonths = function(year) {
14244 	return 12;
14245 };
14246 
14247 /**
14248  * Return the number of days in a particular month in a particular year. This function
14249  * can return a different number for a month depending on the year because of things
14250  * like leap years.
14251  * 
14252  * @param {number} month the month for which the length is sought
14253  * @param {number} year the year within which that month can be found
14254  * @return {number} the number of days within the given month in the given year
14255  */
14256 PersianCal.prototype.getMonLength = function(month, year) {
14257 	if (month !== 12 || !this.isLeapYear(year)) {
14258 		return PersianCal.monthLengths[month-1];
14259 	} else {
14260 		// Month 12, Esfand, has 30 days instead of 29 in leap years
14261 		return 30;
14262 	}
14263 };
14264 
14265 /**
14266  * Return true if the given year is a leap year in the Persian astronomical calendar.
14267  * @param {number} year the year for which the leap year information is being sought
14268  * @return {boolean} true if the given year is a leap year
14269  */
14270 PersianCal.prototype.isLeapYear = function(year) {
14271 	var rdNextYear = new PersRataDie({
14272 		cal: this,
14273 		year: year + 1,
14274 		month: 1,
14275 		day: 1,
14276 		hour: 0,
14277 		minute: 0,
14278 		second: 0,
14279 		millisecond: 0
14280 	});
14281 	var rdThisYear = new PersRataDie({
14282 		cal: this,
14283 		year: year,
14284 		month: 1,
14285 		day: 1,
14286 		hour: 0,
14287 		minute: 0,
14288 		second: 0,
14289 		millisecond: 0
14290 	}); 
14291     return (rdNextYear.getRataDie() - rdThisYear.getRataDie()) > 365;
14292 };
14293 
14294 /**
14295  * Return the type of this calendar.
14296  * 
14297  * @return {string} the name of the type of this calendar 
14298  */
14299 PersianCal.prototype.getType = function() {
14300 	return this.type;
14301 };
14302 
14303 /* register this calendar for the factory method */
14304 Calendar._constructors["persian"] = PersianCal;
14305 
14306 
14307 
14308 /*< PersianDate.js */
14309 /*
14310  * PersianDate.js - Represent a date in the Persian astronomical (Hijjri) calendar
14311  *
14312  * Copyright © 2014-2015, JEDLSoft
14313  *
14314  * Licensed under the Apache License, Version 2.0 (the "License");
14315  * you may not use this file except in compliance with the License.
14316  * You may obtain a copy of the License at
14317  *
14318  *     http://www.apache.org/licenses/LICENSE-2.0
14319  *
14320  * Unless required by applicable law or agreed to in writing, software
14321  * distributed under the License is distributed on an "AS IS" BASIS,
14322  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14323  *
14324  * See the License for the specific language governing permissions and
14325  * limitations under the License.
14326  */
14327 
14328 /* !depends
14329 ilib.js
14330 Locale.js
14331 TimeZone.js
14332 IDate.js
14333 PersRataDie.js
14334 PersianCal.js
14335 SearchUtils.js
14336 MathUtils.js
14337 JSUtils.js
14338 LocaleInfo.js
14339 Astro.js
14340 */
14341 
14342 // !data astro
14343 
14344 
14345 
14346 /**
14347  * @class
14348  *
14349  * Construct a new Persian astronomical date object. The constructor parameters can
14350  * contain any of the following properties:
14351  *
14352  * <ul>
14353  * <li><i>unixtime<i> - sets the time of this instance according to the given
14354  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
14355  *
14356  * <li><i>julianday</i> - sets the time of this instance according to the given
14357  * Julian Day instance or the Julian Day given as a float
14358  *
14359  * <li><i>year</i> - any integer, including 0
14360  *
14361  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
14362  *
14363  * <li><i>day</i> - 1 to 31
14364  *
14365  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
14366  * is always done with an unambiguous 24 hour representation
14367  *
14368  * <li><i>minute</i> - 0 to 59
14369  *
14370  * <li><i>second</i> - 0 to 59
14371  *
14372  * <li><i>millisecond</i> - 0 to 999
14373  *
14374  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string
14375  * of this persian date. The date/time is kept in the local time. The time zone
14376  * is used later if this date is formatted according to a different time zone and
14377  * the difference has to be calculated, or when the date format has a time zone
14378  * component in it.
14379  *
14380  * <li><i>locale</i> - locale for this persian date. If the time zone is not
14381  * given, it can be inferred from this locale. For locales that span multiple
14382  * time zones, the one with the largest population is chosen as the one that
14383  * represents the locale.
14384  *
14385  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
14386  * </ul>
14387  *
14388  * If the constructor is called with another Persian date instance instead of
14389  * a parameter block, the other instance acts as a parameter block and its
14390  * settings are copied into the current instance.<p>
14391  *
14392  * If the constructor is called with no arguments at all or if none of the
14393  * properties listed above
14394  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date
14395  * components are
14396  * filled in with the current date at the time of instantiation. Note that if
14397  * you do not give the time zone when defaulting to the current time and the
14398  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
14399  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich
14400  * Mean Time").<p>
14401  *
14402  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
14403  * specified in the params, it is assumed that they have the smallest possible
14404  * value in the range for the property (zero or one).<p>
14405  *
14406  *
14407  * @constructor
14408  * @extends IDate
14409  * @param {Object=} params parameters that govern the settings and behaviour of this Persian date
14410  */
14411 var PersianDate = function(params) {
14412     this.cal = new PersianCal();
14413 
14414     params = params || {};
14415 
14416     if (params.timezone) {
14417         this.timezone = params.timezone;
14418     }
14419     if (params.locale) {
14420         this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
14421     }
14422 
14423     if (!this.timezone) {
14424         if (this.locale) {
14425             new LocaleInfo(this.locale, {
14426                 sync: params.sync,
14427                 loadParams: params.loadParams,
14428                 onLoad: ilib.bind(this, function(li) {
14429                     this.li = li;
14430                     this.timezone = li.getTimeZone();
14431                     this._init(params);
14432                 })
14433             });
14434         } else {
14435             this.timezone = "local";
14436             this._init(params);
14437         }
14438     } else {
14439         this._init(params);
14440     }
14441 };
14442 
14443 PersianDate.prototype = new IDate({noinstance: true});
14444 PersianDate.prototype.parent = IDate;
14445 PersianDate.prototype.constructor = PersianDate;
14446 
14447 /**
14448  * @private
14449  * Initialize this date object
14450  */
14451 PersianDate.prototype._init = function (params) {
14452     Astro.initAstro(
14453         typeof(params.sync) === 'boolean' ? params.sync : true,
14454         params.loadParams,
14455         ilib.bind(this, function (x) {
14456             if (params.year || params.month || params.day || params.hour ||
14457                     params.minute || params.second || params.millisecond) {
14458                 /**
14459                  * Year in the Persian calendar.
14460                  * @type number
14461                  */
14462                 this.year = parseInt(params.year, 10) || 0;
14463 
14464                 /**
14465                  * The month number, ranging from 1 to 12
14466                  * @type number
14467                  */
14468                 this.month = parseInt(params.month, 10) || 1;
14469 
14470                 /**
14471                  * The day of the month. This ranges from 1 to 31.
14472                  * @type number
14473                  */
14474                 this.day = parseInt(params.day, 10) || 1;
14475 
14476                 /**
14477                  * The hour of the day. This can be a number from 0 to 23, as times are
14478                  * stored unambiguously in the 24-hour clock.
14479                  * @type number
14480                  */
14481                 this.hour = parseInt(params.hour, 10) || 0;
14482 
14483                 /**
14484                  * The minute of the hours. Ranges from 0 to 59.
14485                  * @type number
14486                  */
14487                 this.minute = parseInt(params.minute, 10) || 0;
14488 
14489                 /**
14490                  * The second of the minute. Ranges from 0 to 59.
14491                  * @type number
14492                  */
14493                 this.second = parseInt(params.second, 10) || 0;
14494 
14495                 /**
14496                  * The millisecond of the second. Ranges from 0 to 999.
14497                  * @type number
14498                  */
14499                 this.millisecond = parseInt(params.millisecond, 10) || 0;
14500 
14501                 /**
14502                  * The day of the year. Ranges from 1 to 366.
14503                  * @type number
14504                  */
14505                 this.dayOfYear = parseInt(params.dayOfYear, 10);
14506 
14507                 if (typeof(params.dst) === 'boolean') {
14508                     this.dst = params.dst;
14509                 }
14510 
14511                 this.newRd(JSUtils.merge(params, {
14512                     callback: ilib.bind(this, function(rd) {
14513                         this.rd = rd;
14514 
14515                         new TimeZone({
14516                             id: this.timezone,
14517                             sync: params.sync,
14518                             loadParams: params.loadParams,
14519                             onLoad: ilib.bind(this, function(tz) {
14520                                 this.tz = tz;
14521                                 // add the time zone offset to the rd to convert to UTC
14522                                 // getOffsetMillis requires that this.year, this.rd, and this.dst
14523                                 // are set in order to figure out which time zone rules apply and
14524                                 // what the offset is at that point in the year
14525                                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
14526                                 if (this.offset !== 0) {
14527                                     this.rd = this.newRd({
14528                                         rd: this.rd.getRataDie() - this.offset
14529                                     });
14530                                 }
14531                                 this._init2(params);
14532                             })
14533                         });
14534                     })
14535                 }));
14536 
14537             } else {
14538                 this._init2(params);
14539             }
14540         })
14541     );
14542 };
14543 
14544 /**
14545  * @private
14546  * Finish initializing this date object
14547  */
14548 PersianDate.prototype._init2 = function (params) {
14549     if (!this.rd) {
14550         this.newRd(JSUtils.merge(params, {
14551             callback: ilib.bind(this, function(rd) {
14552                 this.rd = rd;
14553                 this._calcDateComponents();
14554 
14555                 if (typeof(params.onLoad) === "function") {
14556                     params.onLoad(this);
14557                 }
14558             })
14559         }));
14560     } else {
14561         if (typeof(params.onLoad) === "function") {
14562             params.onLoad(this);
14563         }
14564     }
14565 };
14566 
14567 /**
14568  * @private
14569  * @const
14570  * @type Array.<number>
14571  * the cumulative lengths of each month, for a non-leap year
14572  */
14573 PersianDate.cumMonthLengths = [
14574     0,    // Farvardin
14575 	31,   // Ordibehesht
14576 	62,   // Khordad
14577 	93,   // Tir
14578 	124,  // Mordad
14579 	155,  // Shahrivar
14580 	186,  // Mehr
14581 	216,  // Aban
14582 	246,  // Azar
14583 	276,  // Dey
14584 	306,  // Bahman
14585 	336,  // Esfand
14586 	366
14587 ];
14588 
14589 /**
14590  * Return a new RD for this date type using the given params.
14591  * @protected
14592  * @param {Object=} params the parameters used to create this rata die instance
14593  * @returns {RataDie} the new RD instance for the given params
14594  */
14595 PersianDate.prototype.newRd = function (params) {
14596 	return new PersRataDie(params);
14597 };
14598 
14599 /**
14600  * Return the year for the given RD
14601  * @protected
14602  * @param {number} rd RD to calculate from
14603  * @returns {number} the year for the RD
14604  */
14605 PersianDate.prototype._calcYear = function(rd) {
14606 	var julianday = rd + this.rd.epoch;
14607 	return this.rd._getYear(julianday).year;
14608 };
14609 
14610 /**
14611  * @private
14612  * Calculate date components for the given RD date.
14613  */
14614 PersianDate.prototype._calcDateComponents = function () {
14615 	var remainder,
14616 		rd = this.rd.getRataDie();
14617 
14618 	this.year = this._calcYear(rd);
14619 
14620 	if (typeof(this.offset) === "undefined") {
14621 		// now offset the RD by the time zone, then recalculate in case we were
14622 		// near the year boundary
14623 		if (!this.tz) {
14624 			this.tz = new TimeZone({id: this.timezone});
14625 		}
14626 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
14627 	}
14628 
14629 	if (this.offset !== 0) {
14630 		rd += this.offset;
14631 		this.year = this._calcYear(rd);
14632 	}
14633 
14634 	//console.log("PersDate.calcComponent: calculating for rd " + rd);
14635 	//console.log("PersDate.calcComponent: year is " + ret.year);
14636 	var yearStart = this.newRd({
14637 		year: this.year,
14638 		month: 1,
14639 		day: 1,
14640 		hour: 0,
14641 		minute: 0,
14642 		second: 0,
14643 		millisecond: 0
14644 	});
14645 	remainder = rd - yearStart.getRataDie() + 1;
14646 
14647 	this.dayOfYear = remainder;
14648 
14649 	//console.log("PersDate.calcComponent: remainder is " + remainder);
14650 
14651 	this.month = SearchUtils.bsearch(Math.floor(remainder), PersianDate.cumMonthLengths);
14652 	remainder -= PersianDate.cumMonthLengths[this.month-1];
14653 
14654 	//console.log("PersDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
14655 
14656 	this.day = Math.floor(remainder);
14657 	remainder -= this.day;
14658 
14659 	//console.log("PersDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
14660 
14661 	// now convert to milliseconds for the rest of the calculation
14662 	remainder = Math.round(remainder * 86400000);
14663 
14664 	this.hour = Math.floor(remainder/3600000);
14665 	remainder -= this.hour * 3600000;
14666 
14667 	this.minute = Math.floor(remainder/60000);
14668 	remainder -= this.minute * 60000;
14669 
14670 	this.second = Math.floor(remainder/1000);
14671 	remainder -= this.second * 1000;
14672 
14673 	this.millisecond = remainder;
14674 };
14675 
14676 /**
14677  * Return the day of the week of this date. The day of the week is encoded
14678  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
14679  *
14680  * @return {number} the day of the week
14681  */
14682 PersianDate.prototype.getDayOfWeek = function() {
14683 	var rd = Math.floor(this.getRataDie());
14684 	return MathUtils.mod(rd-3, 7);
14685 };
14686 
14687 /**
14688  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to
14689  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and
14690  * December 31st is 365 in regular years, or 366 in leap years.
14691  * @return {number} the ordinal day of the year
14692  */
14693 PersianDate.prototype.getDayOfYear = function() {
14694 	return PersianDate.cumMonthLengths[this.month-1] + this.day;
14695 };
14696 
14697 /**
14698  * Return the era for this date as a number. The value for the era for Persian
14699  * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno
14700  * persico or AP).
14701  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar,
14702  * there is a year 0, so any years that are negative or zero are BP.
14703  * @return {number} 1 if this date is in the common era, -1 if it is before the
14704  * common era
14705  */
14706 PersianDate.prototype.getEra = function() {
14707 	return (this.year < 1) ? -1 : 1;
14708 };
14709 
14710 /**
14711  * Return the name of the calendar that governs this date.
14712  *
14713  * @return {string} a string giving the name of the calendar
14714  */
14715 PersianDate.prototype.getCalendar = function() {
14716 	return "persian";
14717 };
14718 
14719 // register with the factory method
14720 IDate._constructors["persian"] = PersianDate;
14721 
14722 
14723 /*< PersianAlgoCal.js */
14724 /*
14725  * PersianAlgoCal.js - Represent a Persian algorithmic calendar object.
14726  * 
14727  * Copyright © 2014-2015,2018, JEDLSoft
14728  *
14729  * Licensed under the Apache License, Version 2.0 (the "License");
14730  * you may not use this file except in compliance with the License.
14731  * You may obtain a copy of the License at
14732  *
14733  *     http://www.apache.org/licenses/LICENSE-2.0
14734  *
14735  * Unless required by applicable law or agreed to in writing, software
14736  * distributed under the License is distributed on an "AS IS" BASIS,
14737  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14738  *
14739  * See the License for the specific language governing permissions and
14740  * limitations under the License.
14741  */
14742 
14743 
14744 /* !depends Calendar.js MathUtils.js */
14745 
14746 
14747 /**
14748  * @class
14749  * Construct a new Persian algorithmic calendar object. This class encodes information about
14750  * a Persian algorithmic calendar.<p>
14751  * 
14752  * @param {Object=} options Options governing the construction of this instance
14753  * @constructor
14754  * @extends Calendar
14755  */
14756 var PersianAlgoCal = function(options) {
14757 	this.type = "persian-algo";
14758     
14759     if (options && typeof(options.onLoad) === "function") {
14760         options.onLoad(this);
14761     }
14762 };
14763 
14764 /**
14765  * @private
14766  * @const
14767  * @type Array.<number> 
14768  * the lengths of each month 
14769  */
14770 PersianAlgoCal.monthLengths = [
14771 	31,  // Farvardin
14772 	31,  // Ordibehesht
14773 	31,  // Khordad
14774 	31,  // Tir
14775 	31,  // Mordad
14776 	31,  // Shahrivar
14777 	30,  // Mehr
14778 	30,  // Aban
14779 	30,  // Azar
14780 	30,  // Dey
14781 	30,  // Bahman
14782 	29   // Esfand
14783 ];
14784 
14785 /**
14786  * Return the number of months in the given year. The number of months in a year varies
14787  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
14788  * days in a year to an entire solar year. The month is represented as a 1-based number
14789  * where 1=first month, 2=second month, etc.
14790  * 
14791  * @param {number} year a year for which the number of months is sought
14792  * @return {number} The number of months in the given year
14793  */
14794 PersianAlgoCal.prototype.getNumMonths = function(year) {
14795 	return 12;
14796 };
14797 
14798 /**
14799  * Return the number of days in a particular month in a particular year. This function
14800  * can return a different number for a month depending on the year because of things
14801  * like leap years.
14802  * 
14803  * @param {number} month the month for which the length is sought
14804  * @param {number} year the year within which that month can be found
14805  * @return {number} the number of days within the given month in the given year
14806  */
14807 PersianAlgoCal.prototype.getMonLength = function(month, year) {
14808 	if (month !== 12 || !this.isLeapYear(year)) {
14809 		return PersianAlgoCal.monthLengths[month-1];
14810 	} else {
14811 		// Month 12, Esfand, has 30 days instead of 29 in leap years
14812 		return 30;
14813 	}
14814 };
14815 
14816 /**
14817  * Return the equivalent year in the 2820 year cycle that begins on 
14818  * Far 1, 474. This particular cycle obeys the cycle-of-years formula 
14819  * whereas the others do not specifically. This cycle can be used as
14820  * a proxy for other years outside of the cycle by shifting them into 
14821  * the cycle.   
14822  * @param {number} year year to find the equivalent cycle year for
14823  * @returns {number} the equivalent cycle year
14824  */
14825 PersianAlgoCal.prototype.equivalentCycleYear = function(year) {
14826 	var y = year - (year >= 0 ? 474 : 473);
14827 	return MathUtils.mod(y, 2820) + 474;
14828 };
14829 
14830 /**
14831  * Return true if the given year is a leap year in the Persian calendar.
14832  * The year parameter may be given as a number, or as a PersAlgoDate object.
14833  * @param {number} year the year for which the leap year information is being sought
14834  * @return {boolean} true if the given year is a leap year
14835  */
14836 PersianAlgoCal.prototype.isLeapYear = function(year) {
14837 	return (MathUtils.mod((this.equivalentCycleYear(year) + 38) * 682, 2816) < 682);
14838 };
14839 
14840 /**
14841  * Return the type of this calendar.
14842  * 
14843  * @return {string} the name of the type of this calendar 
14844  */
14845 PersianAlgoCal.prototype.getType = function() {
14846 	return this.type;
14847 };
14848 
14849 
14850 /* register this calendar for the factory method */
14851 Calendar._constructors["persian-algo"] = PersianAlgoCal;
14852 
14853 
14854 
14855 /*< PersAlgoRataDie.js */
14856 /*
14857  * PersAlsoRataDie.js - Represent an RD date in the Persian algorithmic calendar
14858  * 
14859  * Copyright © 2014-2015, JEDLSoft
14860  *
14861  * Licensed under the Apache License, Version 2.0 (the "License");
14862  * you may not use this file except in compliance with the License.
14863  * You may obtain a copy of the License at
14864  *
14865  *     http://www.apache.org/licenses/LICENSE-2.0
14866  *
14867  * Unless required by applicable law or agreed to in writing, software
14868  * distributed under the License is distributed on an "AS IS" BASIS,
14869  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14870  *
14871  * See the License for the specific language governing permissions and
14872  * limitations under the License.
14873  */
14874 
14875 /* !depends 
14876 PersianAlgoCal.js 
14877 MathUtils.js
14878 RataDie.js
14879 */
14880 
14881 
14882 /**
14883  * @class
14884  * Construct a new Persian RD date number object. The constructor parameters can 
14885  * contain any of the following properties:
14886  * 
14887  * <ul>
14888  * <li><i>unixtime<i> - sets the time of this instance according to the given 
14889  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
14890  * 
14891  * <li><i>julianday</i> - sets the time of this instance according to the given
14892  * Julian Day instance or the Julian Day given as a float
14893  * 
14894  * <li><i>year</i> - any integer, including 0
14895  * 
14896  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
14897  * 
14898  * <li><i>day</i> - 1 to 31
14899  * 
14900  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
14901  * is always done with an unambiguous 24 hour representation
14902  * 
14903  * <li><i>minute</i> - 0 to 59
14904  * 
14905  * <li><i>second</i> - 0 to 59
14906  * 
14907  * <li><i>millisecond</i> - 0 to 999
14908  * 
14909  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
14910  * </ul>
14911  *
14912  * If the constructor is called with another Persian date instance instead of
14913  * a parameter block, the other instance acts as a parameter block and its
14914  * settings are copied into the current instance.<p>
14915  * 
14916  * If the constructor is called with no arguments at all or if none of the 
14917  * properties listed above are present, then the RD is calculate based on 
14918  * the current date at the time of instantiation. <p>
14919  * 
14920  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
14921  * specified in the params, it is assumed that they have the smallest possible
14922  * value in the range for the property (zero or one).<p>
14923  * 
14924  * 
14925  * @private
14926  * @constructor
14927  * @extends RataDie
14928  * @param {Object=} params parameters that govern the settings and behaviour of this Persian RD date
14929  */
14930 var PersAlgoRataDie = function(params) {
14931 	this.cal = params && params.cal || new PersianAlgoCal();
14932 	this.rd = NaN;
14933 	RataDie.call(this, params);
14934 };
14935 
14936 PersAlgoRataDie.prototype = new RataDie();
14937 PersAlgoRataDie.prototype.parent = RataDie;
14938 PersAlgoRataDie.prototype.constructor = PersAlgoRataDie;
14939 
14940 /**
14941  * The difference between a zero Julian day and the first Persian date
14942  * @private
14943  * @type number
14944  */
14945 PersAlgoRataDie.prototype.epoch = 1948319.5;
14946 
14947 /**
14948  * @private
14949  * @const
14950  * @type Array.<number>
14951  * the cumulative lengths of each month, for a non-leap year 
14952  */
14953 PersAlgoRataDie.cumMonthLengths = [
14954     0,    // Farvardin
14955 	31,   // Ordibehesht
14956 	62,   // Khordad
14957 	93,   // Tir
14958 	124,  // Mordad
14959 	155,  // Shahrivar
14960 	186,  // Mehr
14961 	216,  // Aban
14962 	246,  // Azar
14963 	276,  // Dey
14964 	306,  // Bahman
14965 	336,  // Esfand
14966 	365
14967 ];
14968 
14969 /**
14970  * Calculate the Rata Die (fixed day) number of the given date from the
14971  * date components.
14972  *
14973  * @protected
14974  * @param {Object} date the date components to calculate the RD from
14975  */
14976 PersAlgoRataDie.prototype._setDateComponents = function(date) {
14977 	var year = this.cal.equivalentCycleYear(date.year);
14978 	var y = date.year - (date.year >= 0 ? 474 : 473);
14979 	var rdOfYears = 1029983 * Math.floor(y/2820) + 365 * (year - 1) + Math.floor((682 * year - 110) / 2816);
14980 	var dayInYear = (date.month > 1 ? PersAlgoRataDie.cumMonthLengths[date.month-1] : 0) + date.day;
14981 	var rdtime = (date.hour * 3600000 +
14982 		date.minute * 60000 +
14983 		date.second * 1000 +
14984 		date.millisecond) /
14985 		86400000;
14986 	
14987 	/*
14988 	// console.log("getRataDie: converting " +  JSON.stringify(this));
14989 	console.log("getRataDie: year is " +  year);
14990 	console.log("getRataDie: rd of years is " +  rdOfYears);
14991 	console.log("getRataDie: day in year is " +  dayInYear);
14992 	console.log("getRataDie: rdtime is " +  rdtime);
14993 	console.log("getRataDie: rd is " +  (rdOfYears + dayInYear + rdtime));
14994 	*/
14995 	
14996 	this.rd = rdOfYears + dayInYear + rdtime;
14997 };
14998 
14999 /**
15000  * Return the rd number of the particular day of the week on or before the 
15001  * given rd. eg. The Sunday on or before the given rd.
15002  * @private
15003  * @param {number} rd the rata die date of the reference date
15004  * @param {number} dayOfWeek the day of the week that is being sought relative 
15005  * to the current date
15006  * @return {number} the rd of the day of the week
15007  */
15008 PersAlgoRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
15009 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek - 3, 7);
15010 };
15011 
15012 
15013 /*< PersianAlgoDate.js */
15014 /*
15015  * PersianAlgoDate.js - Represent a date in the Persian algorithmic calendar
15016  * 
15017  * Copyright © 2014-2015, JEDLSoft
15018  *
15019  * Licensed under the Apache License, Version 2.0 (the "License");
15020  * you may not use this file except in compliance with the License.
15021  * You may obtain a copy of the License at
15022  *
15023  *     http://www.apache.org/licenses/LICENSE-2.0
15024  *
15025  * Unless required by applicable law or agreed to in writing, software
15026  * distributed under the License is distributed on an "AS IS" BASIS,
15027  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15028  *
15029  * See the License for the specific language governing permissions and
15030  * limitations under the License.
15031  */
15032 
15033 /* !depends 
15034 ilib.js
15035 Locale.js
15036 LocaleInfo.js
15037 TimeZone.js
15038 IDate.js
15039 PersianAlgoCal.js 
15040 SearchUtils.js
15041 MathUtils.js
15042 PersAlgoRataDie.js
15043 */
15044 
15045 
15046 
15047 
15048 /**
15049  * @class
15050  * 
15051  * Construct a new Persian date object. The constructor parameters can 
15052  * contain any of the following properties:
15053  * 
15054  * <ul>
15055  * <li><i>unixtime<i> - sets the time of this instance according to the given 
15056  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
15057  * 
15058  * <li><i>julianday</i> - sets the time of this instance according to the given
15059  * Julian Day instance or the Julian Day given as a float
15060  * 
15061  * <li><i>year</i> - any integer, including 0
15062  * 
15063  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
15064  * 
15065  * <li><i>day</i> - 1 to 31
15066  * 
15067  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
15068  * is always done with an unambiguous 24 hour representation
15069  * 
15070  * <li><i>minute</i> - 0 to 59
15071  * 
15072  * <li><i>second</i> - 0 to 59
15073  * 
15074  * <li><i>millisecond</i> - 0 to 999
15075  * 
15076  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
15077  * of this persian date. The date/time is kept in the local time. The time zone
15078  * is used later if this date is formatted according to a different time zone and
15079  * the difference has to be calculated, or when the date format has a time zone
15080  * component in it.
15081  * 
15082  * <li><i>locale</i> - locale for this persian date. If the time zone is not 
15083  * given, it can be inferred from this locale. For locales that span multiple
15084  * time zones, the one with the largest population is chosen as the one that 
15085  * represents the locale.
15086  * 
15087  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
15088  * </ul>
15089  *
15090  * If the constructor is called with another Persian date instance instead of
15091  * a parameter block, the other instance acts as a parameter block and its
15092  * settings are copied into the current instance.<p>
15093  * 
15094  * If the constructor is called with no arguments at all or if none of the 
15095  * properties listed above 
15096  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
15097  * components are 
15098  * filled in with the current date at the time of instantiation. Note that if
15099  * you do not give the time zone when defaulting to the current time and the 
15100  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
15101  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
15102  * Mean Time").<p>
15103  * 
15104  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
15105  * specified in the params, it is assumed that they have the smallest possible
15106  * value in the range for the property (zero or one).<p>
15107  * 
15108  * 
15109  * @constructor
15110  * @extends IDate
15111  * @param {Object=} params parameters that govern the settings and behaviour of this Persian date
15112  */
15113 var PersianAlgoDate = function(params) {
15114 	this.cal = new PersianAlgoCal();
15115 	
15116 	params = params || {};
15117 	
15118 	if (params.timezone) {
15119 	    this.timezone = params.timezone;
15120 	}
15121 	if (params.locale) {
15122 	    this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
15123 	}
15124 	
15125     if (!this.timezone) {
15126         if (this.locale) {
15127             new LocaleInfo(this.locale, {
15128                 sync: params.sync,
15129                 loadParams: params.loadParams,
15130                 onLoad: ilib.bind(this, function(li) {
15131                     this.li = li;
15132                     this.timezone = li.getTimeZone();
15133                     this._init(params);
15134                 })
15135             });
15136         } else {
15137             this.timezone = "local";
15138             this._init(params);
15139         }
15140     } else {
15141         this._init(params);
15142     }
15143 
15144 
15145 	if (!this.rd) {
15146 		this.rd = this.newRd(params);
15147 		this._calcDateComponents();
15148 	}
15149 };
15150 
15151 PersianAlgoDate.prototype = new IDate({noinstance: true});
15152 PersianAlgoDate.prototype.parent = IDate;
15153 PersianAlgoDate.prototype.constructor = PersianAlgoDate;
15154 
15155 /**
15156  * Initialize this date
15157  * @private
15158  */
15159 PersianAlgoDate.prototype._init = function (params) {
15160     if (params.year || params.month || params.day || params.hour ||
15161         params.minute || params.second || params.millisecond ) {
15162         /**
15163          * Year in the Persian calendar.
15164          * @type number
15165          */
15166         this.year = parseInt(params.year, 10) || 0;
15167 
15168         /**
15169          * The month number, ranging from 1 to 12
15170          * @type number
15171          */
15172         this.month = parseInt(params.month, 10) || 1;
15173 
15174         /**
15175          * The day of the month. This ranges from 1 to 31.
15176          * @type number
15177          */
15178         this.day = parseInt(params.day, 10) || 1;
15179 
15180         /**
15181          * The hour of the day. This can be a number from 0 to 23, as times are
15182          * stored unambiguously in the 24-hour clock.
15183          * @type number
15184          */
15185         this.hour = parseInt(params.hour, 10) || 0;
15186 
15187         /**
15188          * The minute of the hours. Ranges from 0 to 59.
15189          * @type number
15190          */
15191         this.minute = parseInt(params.minute, 10) || 0;
15192 
15193         /**
15194          * The second of the minute. Ranges from 0 to 59.
15195          * @type number
15196          */
15197         this.second = parseInt(params.second, 10) || 0;
15198 
15199         /**
15200          * The millisecond of the second. Ranges from 0 to 999.
15201          * @type number
15202          */
15203         this.millisecond = parseInt(params.millisecond, 10) || 0;
15204 
15205         /**
15206          * The day of the year. Ranges from 1 to 366.
15207          * @type number
15208          */
15209         this.dayOfYear = parseInt(params.dayOfYear, 10);
15210 
15211         if (typeof(params.dst) === 'boolean') {
15212             this.dst = params.dst;
15213         }
15214 
15215         this.rd = this.newRd(this);
15216 
15217         new TimeZone({
15218             id: this.timezone,
15219             sync: params.sync,
15220             loadParams: params.loadParams,
15221             onLoad: ilib.bind(this, function(tz) {
15222                 this.tz = tz;
15223                 // add the time zone offset to the rd to convert to UTC
15224                 // getOffsetMillis requires that this.year, this.rd, and this.dst 
15225                 // are set in order to figure out which time zone rules apply and 
15226                 // what the offset is at that point in the year
15227                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
15228                 if (this.offset !== 0) {
15229                     this.rd = this.newRd({
15230                         rd: this.rd.getRataDie() - this.offset
15231                     });
15232                 }
15233                 this._init2(params);
15234             })
15235         });
15236     } else {
15237         this._init2(params);
15238     }
15239 };
15240 
15241 /**
15242  * @private
15243  * Finish initializing this date object
15244  */
15245 PersianAlgoDate.prototype._init2 = function (params) {
15246     if (!this.rd) {
15247         this.rd = this.newRd(params);
15248         this._calcDateComponents();
15249     }
15250 
15251     if (typeof(params.onLoad) === "function") {
15252         params.onLoad(this);
15253     }
15254 };
15255 
15256 /**
15257  * Return a new RD for this date type using the given params.
15258  * @protected
15259  * @param {Object=} params the parameters used to create this rata die instance
15260  * @returns {RataDie} the new RD instance for the given params
15261  */
15262 PersianAlgoDate.prototype.newRd = function (params) {
15263 	return new PersAlgoRataDie(params);
15264 };
15265 
15266 /**
15267  * Return the year for the given RD
15268  * @protected
15269  * @param {number} rd RD to calculate from 
15270  * @returns {number} the year for the RD
15271  */
15272 PersianAlgoDate.prototype._calcYear = function(rd) {
15273 	var shiftedRd = rd - 173126;
15274 	var numberOfCycles = Math.floor(shiftedRd / 1029983);
15275 	var shiftedDayInCycle = MathUtils.mod(shiftedRd, 1029983);
15276 	var yearInCycle = (shiftedDayInCycle === 1029982) ? 2820 : Math.floor((2816 * shiftedDayInCycle + 1031337) / 1028522);
15277 	var year = 474 + 2820 * numberOfCycles + yearInCycle;
15278 	return (year > 0) ? year : year - 1;
15279 };
15280 
15281 /**
15282  * @private
15283  * Calculate date components for the given RD date.
15284  */
15285 PersianAlgoDate.prototype._calcDateComponents = function () {
15286 	var remainder,
15287 		rd = this.rd.getRataDie();
15288 	
15289 	this.year = this._calcYear(rd);
15290 	
15291 	if (typeof(this.offset) === "undefined") {
15292 		// now offset the RD by the time zone, then recalculate in case we were 
15293 		// near the year boundary
15294 		if (!this.tz) {
15295 			this.tz = new TimeZone({id: this.timezone});
15296 		}
15297 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
15298 	}
15299 	
15300 	if (this.offset !== 0) {
15301 		rd += this.offset;
15302 		this.year = this._calcYear(rd);
15303 	}
15304 	
15305 	//console.log("PersAlgoDate.calcComponent: calculating for rd " + rd);
15306 	//console.log("PersAlgoDate.calcComponent: year is " + ret.year);
15307 	var yearStart = this.newRd({
15308 		year: this.year,
15309 		month: 1,
15310 		day: 1,
15311 		hour: 0,
15312 		minute: 0,
15313 		second: 0,
15314 		millisecond: 0
15315 	});
15316 	remainder = rd - yearStart.getRataDie() + 1;
15317 	
15318 	this.dayOfYear = remainder;
15319 	
15320 	//console.log("PersAlgoDate.calcComponent: remainder is " + remainder);
15321 	
15322 	this.month = SearchUtils.bsearch(remainder, PersAlgoRataDie.cumMonthLengths);
15323 	remainder -= PersAlgoRataDie.cumMonthLengths[this.month-1];
15324 	
15325 	//console.log("PersAlgoDate.calcComponent: month is " + this.month + " and remainder is " + remainder);
15326 	
15327 	this.day = Math.floor(remainder);
15328 	remainder -= this.day;
15329 	
15330 	//console.log("PersAlgoDate.calcComponent: day is " + this.day + " and remainder is " + remainder);
15331 	
15332 	// now convert to milliseconds for the rest of the calculation
15333 	remainder = Math.round(remainder * 86400000);
15334 	
15335 	this.hour = Math.floor(remainder/3600000);
15336 	remainder -= this.hour * 3600000;
15337 	
15338 	this.minute = Math.floor(remainder/60000);
15339 	remainder -= this.minute * 60000;
15340 	
15341 	this.second = Math.floor(remainder/1000);
15342 	remainder -= this.second * 1000;
15343 	
15344 	this.millisecond = remainder;
15345 };
15346 
15347 /**
15348  * Return the day of the week of this date. The day of the week is encoded
15349  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
15350  * 
15351  * @return {number} the day of the week
15352  */
15353 PersianAlgoDate.prototype.getDayOfWeek = function() {
15354 	var rd = Math.floor(this.getRataDie());
15355 	return MathUtils.mod(rd-3, 7);
15356 };
15357 
15358 /**
15359  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
15360  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 
15361  * December 31st is 365 in regular years, or 366 in leap years.
15362  * @return {number} the ordinal day of the year
15363  */
15364 PersianAlgoDate.prototype.getDayOfYear = function() {
15365 	return PersAlgoRataDie.cumMonthLengths[this.month-1] + this.day;
15366 };
15367 
15368 /**
15369  * Return the era for this date as a number. The value for the era for Persian 
15370  * calendars is -1 for "before the persian era" (BP) and 1 for "the persian era" (anno 
15371  * persico or AP). 
15372  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Persian calendar, 
15373  * there is a year 0, so any years that are negative or zero are BP.
15374  * @return {number} 1 if this date is in the common era, -1 if it is before the 
15375  * common era 
15376  */
15377 PersianAlgoDate.prototype.getEra = function() {
15378 	return (this.year < 1) ? -1 : 1;
15379 };
15380 
15381 /**
15382  * Return the name of the calendar that governs this date.
15383  * 
15384  * @return {string} a string giving the name of the calendar
15385  */
15386 PersianAlgoDate.prototype.getCalendar = function() {
15387 	return "persian-algo";
15388 };
15389 
15390 // register with the factory method
15391 IDate._constructors["persian-algo"] = PersianAlgoDate;
15392 
15393 
15394 /*< HanCal.js */
15395 /*
15396  * han.js - Represent a Han Chinese Lunar calendar object.
15397  * 
15398  * Copyright © 2014-2015, JEDLSoft
15399  *
15400  * Licensed under the Apache License, Version 2.0 (the "License");
15401  * you may not use this file except in compliance with the License.
15402  * You may obtain a copy of the License at
15403  *
15404  *     http://www.apache.org/licenses/LICENSE-2.0
15405  *
15406  * Unless required by applicable law or agreed to in writing, software
15407  * distributed under the License is distributed on an "AS IS" BASIS,
15408  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15409  *
15410  * See the License for the specific language governing permissions and
15411  * limitations under the License.
15412  */
15413 
15414 /* !depends 
15415 ilib.js 
15416 Calendar.js 
15417 MathUtils.js 
15418 Astro.js
15419 GregorianDate.js
15420 GregRataDie.js
15421 RataDie.js
15422 */
15423 
15424 
15425 
15426 
15427 /**
15428  * @class
15429  * Construct a new Han algorithmic calendar object. This class encodes information about
15430  * a Han algorithmic calendar.<p>
15431  * 
15432  * 
15433  * @constructor
15434  * @param {Object=} params optional parameters to load the calendrical data
15435  * @extends Calendar
15436  */
15437 var HanCal = function(params) {
15438 	this.type = "han";
15439 	var sync = params && typeof(params.sync) === 'boolean' ? params.sync : true;
15440 	
15441 	Astro.initAstro(sync, params && params.loadParams, ilib.bind(this, function (x) {
15442 		if (params && typeof(params.onLoad) === 'function') {
15443 			params.onLoad(this);
15444 		}
15445 	}));
15446 };
15447 
15448 /**
15449  * @protected
15450  * @static
15451  * @param {number} year
15452  * @param {number=} cycle
15453  * @return {number}
15454  */
15455 HanCal._getElapsedYear = function(year, cycle) {
15456 	var elapsedYear = year || 0;
15457 	if (typeof(year) !== 'undefined' && year < 61 && typeof(cycle) !== 'undefined') {
15458 		elapsedYear = 60 * cycle + year;
15459 	}
15460 	return elapsedYear;
15461 };
15462 
15463 /**
15464  * @protected
15465  * @static
15466  * @param {number} jd julian day to calculate from
15467  * @param {number} longitude longitude to seek 
15468  * @returns {number} the julian day of the next time that the solar longitude 
15469  * is a multiple of the given longitude
15470  */
15471 HanCal._hanNextSolarLongitude = function(jd, longitude) {
15472 	var tz = HanCal._chineseTZ(jd);
15473 	var uni = Astro._universalFromLocal(jd, tz);
15474 	var sol = Astro._nextSolarLongitude(uni, longitude);
15475 	return Astro._localFromUniversal(sol, tz);
15476 };
15477 
15478 /**
15479  * @protected
15480  * @static
15481  * @param {number} jd julian day to calculate from 
15482  * @returns {number} the major solar term for the julian day
15483  */
15484 HanCal._majorSTOnOrAfter = function(jd) {
15485 	var tz = HanCal._chineseTZ(jd);
15486 	var uni = Astro._universalFromLocal(jd, tz);
15487 	var next = Astro._fixangle(30 * Math.ceil(Astro._solarLongitude(uni)/30));
15488 	return HanCal._hanNextSolarLongitude(jd, next);
15489 };
15490 
15491 /**
15492  * @protected
15493  * @static
15494  * @param {number} year the year for which the leap year information is being sought
15495  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15496  * cycle is not given, then the year should be given as elapsed years since the beginning
15497  * of the epoch
15498  */
15499 HanCal._solsticeBefore = function (year, cycle) {
15500 	var elapsedYear = HanCal._getElapsedYear(year, cycle);
15501 	var gregyear = elapsedYear - 2697;
15502 	var rd = new GregRataDie({
15503 		year: gregyear-1, 
15504 		month: 12, 
15505 		day: 15, 
15506 		hour: 0, 
15507 		minute: 0, 
15508 		second: 0, 
15509 		millisecond: 0
15510 	});
15511 	return HanCal._majorSTOnOrAfter(rd.getRataDie() + RataDie.gregorianEpoch);
15512 };
15513 
15514 /**
15515  * @protected
15516  * @static
15517  * @param {number} jd julian day to calculate from
15518  * @returns {number} the current major solar term
15519  */
15520 HanCal._chineseTZ = function(jd) {
15521 	var year = GregorianDate._calcYear(jd - RataDie.gregorianEpoch);
15522 	return year < 1929 ? 465.6666666666666666 : 480;
15523 };
15524 
15525 /**
15526  * @protected
15527  * @static
15528  * @param {number} jd julian day to calculate from 
15529  * @returns {number} the julian day of next new moon on or after the given julian day date
15530  */
15531 HanCal._newMoonOnOrAfter = function(jd) {
15532 	var tz = HanCal._chineseTZ(jd);
15533 	var uni = Astro._universalFromLocal(jd, tz);
15534 	var moon = Astro._newMoonAtOrAfter(uni);
15535 	// floor to the start of the julian day
15536 	return Astro._floorToJD(Astro._localFromUniversal(moon, tz)); 
15537 };
15538 
15539 /**
15540  * @protected
15541  * @static
15542  * @param {number} jd julian day to calculate from 
15543  * @returns {number} the julian day of previous new moon before the given julian day date
15544  */
15545 HanCal._newMoonBefore = function(jd) {
15546 	var tz = HanCal._chineseTZ(jd);
15547 	var uni = Astro._universalFromLocal(jd, tz);
15548 	var moon = Astro._newMoonBefore(uni);
15549 	// floor to the start of the julian day
15550 	return Astro._floorToJD(Astro._localFromUniversal(moon, tz));
15551 };
15552 
15553 /**
15554  * @static
15555  * @protected
15556  * @param {number} year the year for which the leap year information is being sought
15557  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15558  * cycle is not given, then the year should be given as elapsed years since the beginning
15559  * of the epoch
15560  */
15561 HanCal._leapYearCalc = function(year, cycle) {
15562 	var ret = {
15563 		elapsedYear: HanCal._getElapsedYear(year, cycle)
15564 	};
15565 	ret.solstice1 = HanCal._solsticeBefore(ret.elapsedYear);
15566 	ret.solstice2 = HanCal._solsticeBefore(ret.elapsedYear+1);
15567 	// ceil to the end of the julian day
15568 	ret.m1 = HanCal._newMoonOnOrAfter(Astro._ceilToJD(ret.solstice1));
15569 	ret.m2 = HanCal._newMoonBefore(Astro._ceilToJD(ret.solstice2));
15570 	
15571 	return ret;
15572 };
15573 
15574 /**
15575  * @protected
15576  * @static
15577  * @param {number} jd julian day to calculate from
15578  * @returns {number} the current major solar term
15579  */
15580 HanCal._currentMajorST = function(jd) {
15581 	var s = Astro._solarLongitude(Astro._universalFromLocal(jd, HanCal._chineseTZ(jd)));
15582 	return MathUtils.amod(2 + Math.floor(s/30), 12);
15583 };
15584 
15585 /**
15586  * @protected
15587  * @static
15588  * @param {number} jd julian day to calculate from
15589  * @returns {boolean} true if there is no major solar term in the same year
15590  */
15591 HanCal._noMajorST = function(jd) {
15592 	return HanCal._currentMajorST(jd) === HanCal._currentMajorST(HanCal._newMoonOnOrAfter(jd+1));
15593 };
15594 
15595 /**
15596  * Return the number of months in the given year. The number of months in a year varies
15597  * for some luni-solar calendars because in some years, an extra month is needed to extend the 
15598  * days in a year to an entire solar year. The month is represented as a 1-based number
15599  * where 1=first month, 2=second month, etc.
15600  * 
15601  * @param {number} year a year for which the number of months is sought
15602  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15603  * cycle is not given, then the year should be given as elapsed years since the beginning
15604  * of the epoch
15605  * @return {number} The number of months in the given year
15606  */
15607 HanCal.prototype.getNumMonths = function(year, cycle) {
15608 	return this.isLeapYear(year, cycle) ? 13 : 12;
15609 };
15610 
15611 /**
15612  * Return the number of days in a particular month in a particular year. This function
15613  * can return a different number for a month depending on the year because of things
15614  * like leap years.
15615  * 
15616  * @param {number} month the elapsed month for which the length is sought
15617  * @param {number} year the elapsed year within which that month can be found
15618  * @return {number} the number of days within the given month in the given year
15619  */
15620 HanCal.prototype.getMonLength = function(month, year) {
15621 	// distance between two new moons in Nanjing China
15622 	var calc = HanCal._leapYearCalc(year);
15623 	var priorNewMoon = HanCal._newMoonOnOrAfter(calc.m1 + month * 29);
15624 	var postNewMoon = HanCal._newMoonOnOrAfter(priorNewMoon + 1);
15625 	return postNewMoon - priorNewMoon;
15626 };
15627 
15628 /**
15629  * Return the equivalent year in the 2820 year cycle that begins on 
15630  * Far 1, 474. This particular cycle obeys the cycle-of-years formula 
15631  * whereas the others do not specifically. This cycle can be used as
15632  * a proxy for other years outside of the cycle by shifting them into 
15633  * the cycle.   
15634  * @param {number} year year to find the equivalent cycle year for
15635  * @returns {number} the equivalent cycle year
15636  */
15637 HanCal.prototype.equivalentCycleYear = function(year) {
15638 	var y = year - (year >= 0 ? 474 : 473);
15639 	return MathUtils.mod(y, 2820) + 474;
15640 };
15641 
15642 /**
15643  * Return true if the given year is a leap year in the Han calendar.
15644  * If the year is given as a year/cycle combination, then the year should be in the 
15645  * range [1,60] and the given cycle is the cycle in which the year is located. If 
15646  * the year is greater than 60, then
15647  * it represents the total number of years elapsed in the proleptic calendar since
15648  * the beginning of the Chinese epoch in on 15 Feb, -2636 (Gregorian). In this 
15649  * case, the cycle parameter is ignored.
15650  * 
15651  * @param {number} year the year for which the leap year information is being sought
15652  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15653  * cycle is not given, then the year should be given as elapsed years since the beginning
15654  * of the epoch
15655  * @return {boolean} true if the given year is a leap year
15656  */
15657 HanCal.prototype.isLeapYear = function(year, cycle) {
15658 	var calc = HanCal._leapYearCalc(year, cycle);
15659 	return Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
15660 };
15661 
15662 /**
15663  * Return the month of the year that is the leap month. If the given year is
15664  * not a leap year, then this method will return -1.
15665  * 
15666  * @param {number} year the year for which the leap year information is being sought
15667  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15668  * cycle is not given, then the year should be given as elapsed years since the beginning
15669  * of the epoch
15670  * @return {number} the number of the month that is doubled in this leap year, or -1
15671  * if this is not a leap year
15672  */
15673 HanCal.prototype.getLeapMonth = function(year, cycle) {
15674 	var calc = HanCal._leapYearCalc(year, cycle);
15675 	
15676 	if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) != 12) {
15677 		return -1; // no leap month
15678 	}
15679 	
15680 	// search between rd1 and rd2 for the first month with no major solar term. That is our leap month.
15681 	var month = 0;
15682 	var m = HanCal._newMoonOnOrAfter(calc.m1+1);
15683 	while (!HanCal._noMajorST(m)) {
15684 		month++;
15685 		m = HanCal._newMoonOnOrAfter(m+1);
15686 	}
15687 	
15688 	// return the number of the month that is doubled
15689 	return month; 
15690 };
15691 
15692 /**
15693  * Return the date of Chinese New Years in the given calendar year.
15694  * 
15695  * @param {number} year the Chinese year for which the new year information is being sought
15696  * @param {number=} cycle if the given year < 60, this can specify the cycle. If the
15697  * cycle is not given, then the year should be given as elapsed years since the beginning
15698  * of the epoch
15699  * @return {number} the julian day of the beginning of the given year 
15700  */
15701 HanCal.prototype.newYears = function(year, cycle) {
15702 	var calc = HanCal._leapYearCalc(year, cycle);
15703 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15704 	if (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12 &&
15705 			(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2)) ) {
15706 		return HanCal._newMoonOnOrAfter(m2+1);
15707 	}
15708 	return m2;
15709 };
15710 
15711 /**
15712  * Return the type of this calendar.
15713  * 
15714  * @return {string} the name of the type of this calendar 
15715  */
15716 HanCal.prototype.getType = function() {
15717 	return this.type;
15718 };
15719 
15720 
15721 /* register this calendar for the factory method */
15722 Calendar._constructors["han"] = HanCal;
15723 
15724 
15725 
15726 /*< HanRataDie.js */
15727 /*
15728  * HanDate.js - Represent a date in the Han algorithmic calendar
15729  * 
15730  * Copyright © 2014-2015, JEDLSoft
15731  *
15732  * Licensed under the Apache License, Version 2.0 (the "License");
15733  * you may not use this file except in compliance with the License.
15734  * You may obtain a copy of the License at
15735  *
15736  *     http://www.apache.org/licenses/LICENSE-2.0
15737  *
15738  * Unless required by applicable law or agreed to in writing, software
15739  * distributed under the License is distributed on an "AS IS" BASIS,
15740  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15741  *
15742  * See the License for the specific language governing permissions and
15743  * limitations under the License.
15744  */
15745 
15746 /* !depends
15747 ilib.js
15748 HanCal.js
15749 MathUtils.js
15750 RataDie.js
15751 */
15752 
15753 
15754 /**
15755  * Construct a new Han RD date number object. The constructor parameters can 
15756  * contain any of the following properties:
15757  * 
15758  * <ul>
15759  * <li><i>unixtime<i> - sets the time of this instance according to the given 
15760  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
15761  * 
15762  * <li><i>julianday</i> - sets the time of this instance according to the given
15763  * Julian Day instance or the Julian Day given as a float
15764  * 
15765  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
15766  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
15767  * linear count of years since the beginning of the epoch, much like other calendars. This linear
15768  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
15769  * to 60 and treated as if it were a year in the regular 60-year cycle.
15770  * 
15771  * <li><i>year</i> - any integer, including 0
15772  * 
15773  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
15774  * 
15775  * <li><i>day</i> - 1 to 31
15776  * 
15777  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
15778  * is always done with an unambiguous 24 hour representation
15779  * 
15780  * <li><i>minute</i> - 0 to 59
15781  * 
15782  * <li><i>second</i> - 0 to 59
15783  * 
15784  * <li><i>millisecond</i> - 0 to 999
15785  * 
15786  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
15787  * </ul>
15788  *
15789  * If the constructor is called with another Han date instance instead of
15790  * a parameter block, the other instance acts as a parameter block and its
15791  * settings are copied into the current instance.<p>
15792  * 
15793  * If the constructor is called with no arguments at all or if none of the 
15794  * properties listed above are present, then the RD is calculate based on 
15795  * the current date at the time of instantiation. <p>
15796  * 
15797  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
15798  * specified in the params, it is assumed that they have the smallest possible
15799  * value in the range for the property (zero or one).<p>
15800  * 
15801  * 
15802  * @private
15803  * @class
15804  * @constructor
15805  * @extends RataDie
15806  * @param {Object=} params parameters that govern the settings and behaviour of this Han RD date
15807  */
15808 var HanRataDie = function(params) {
15809 	this.rd = NaN;
15810 	if (params && params.cal) {
15811 		this.cal = params.cal;
15812 		RataDie.call(this, params);
15813 		if (params && typeof(params.callback) === 'function') {
15814 			params.callback(this);
15815 		}
15816 	} else {
15817 		new HanCal({
15818 			sync: params && params.sync,
15819 			loadParams: params && params.loadParams,
15820 			onLoad: ilib.bind(this, function(c) {
15821 				this.cal = c;
15822 				RataDie.call(this, params);
15823 				if (params && typeof(params.callback) === 'function') {
15824 					params.callback(this);
15825 				}
15826 			})
15827 		});
15828 	}
15829 };
15830 
15831 HanRataDie.prototype = new RataDie();
15832 HanRataDie.prototype.parent = RataDie;
15833 HanRataDie.prototype.constructor = HanRataDie;
15834 
15835 /**
15836  * The difference between a zero Julian day and the first Han date
15837  * which is February 15, -2636 (Gregorian).
15838  * @private
15839  * @type number
15840  */
15841 HanRataDie.epoch = 758325.5;
15842 
15843 /**
15844  * Calculate the Rata Die (fixed day) number of the given date from the
15845  * date components.
15846  *
15847  * @protected
15848  * @param {Object} date the date components to calculate the RD from
15849  */
15850 HanRataDie.prototype._setDateComponents = function(date) {
15851 	var calc = HanCal._leapYearCalc(date.year, date.cycle);
15852 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
15853 	var newYears;
15854 	this.leapYear = (Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12);
15855 	if (this.leapYear && (HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2)) ) {
15856 		newYears = HanCal._newMoonOnOrAfter(m2+1);
15857 	} else {
15858 		newYears = m2;
15859 	}
15860 
15861 	var priorNewMoon = HanCal._newMoonOnOrAfter(calc.m1 + date.month * 29); // this is a julian day
15862 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(priorNewMoon));
15863 	this.leapMonth = (this.leapYear && HanCal._noMajorST(priorNewMoon) && !this.priorLeapMonth);
15864 
15865 	var rdtime = (date.hour * 3600000 +
15866 		date.minute * 60000 +
15867 		date.second * 1000 +
15868 		date.millisecond) /
15869 		86400000;
15870 	
15871 	/*
15872 	console.log("getRataDie: converting " +  JSON.stringify(date) + " to an RD");
15873 	console.log("getRataDie: year is " +  date.year + " plus cycle " + date.cycle);
15874 	console.log("getRataDie: isLeapYear is " +  this.leapYear);
15875 	console.log("getRataDie: priorNewMoon is " +  priorNewMoon);
15876 	console.log("getRataDie: day in month is " +  date.day);
15877 	console.log("getRataDie: rdtime is " +  rdtime);
15878 	console.log("getRataDie: rd is " +  (priorNewMoon + date.day - 1 + rdtime));
15879 	*/
15880 	
15881 	this.rd = priorNewMoon + date.day - 1 + rdtime - RataDie.gregorianEpoch;
15882 };
15883 
15884 /**
15885  * Return the rd number of the particular day of the week on or before the 
15886  * given rd. eg. The Sunday on or before the given rd.
15887  * @private
15888  * @param {number} rd the rata die date of the reference date
15889  * @param {number} dayOfWeek the day of the week that is being sought relative 
15890  * to the current date
15891  * @return {number} the rd of the day of the week
15892  */
15893 HanRataDie.prototype._onOrBefore = function(rd, dayOfWeek) {
15894 	return rd - MathUtils.mod(Math.floor(rd) - dayOfWeek, 7);
15895 };
15896 
15897 /**
15898  * @protected
15899  * @static
15900  * @param {number} jd1 first julian day
15901  * @param {number} jd2 second julian day
15902  * @returns {boolean} true if there is a leap month earlier in the same year 
15903  * as the given months 
15904  */
15905 HanRataDie._priorLeapMonth = function(jd1, jd2) {
15906 	return jd2 >= jd1 &&
15907 		(HanRataDie._priorLeapMonth(jd1, HanCal._newMoonBefore(jd2)) ||
15908 				HanCal._noMajorST(jd2));
15909 };
15910 
15911 
15912 
15913 /*< HanDate.js */
15914 /*
15915  * HanDate.js - Represent a date in the Han algorithmic calendar
15916  * 
15917  * Copyright © 2014-2015, JEDLSoft
15918  *
15919  * Licensed under the Apache License, Version 2.0 (the "License");
15920  * you may not use this file except in compliance with the License.
15921  * You may obtain a copy of the License at
15922  *
15923  *     http://www.apache.org/licenses/LICENSE-2.0
15924  *
15925  * Unless required by applicable law or agreed to in writing, software
15926  * distributed under the License is distributed on an "AS IS" BASIS,
15927  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15928  *
15929  * See the License for the specific language governing permissions and
15930  * limitations under the License.
15931  */
15932 
15933 /* !depends
15934 ilib.js
15935 IDate.js
15936 GregorianDate.js 
15937 HanCal.js
15938 Astro.js 
15939 JSUtils.js
15940 MathUtils.js
15941 LocaleInfo.js 
15942 Locale.js
15943 TimeZone.js
15944 HanRataDie.js
15945 RataDie.js
15946 */
15947 
15948 
15949 
15950 
15951 /**
15952  * @class
15953  * 
15954  * Construct a new Han date object. The constructor parameters can 
15955  * contain any of the following properties:
15956  * 
15957  * <ul>
15958  * <li><i>unixtime<i> - sets the time of this instance according to the given 
15959  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970, Gregorian
15960  * 
15961  * <li><i>julianday</i> - sets the time of this instance according to the given
15962  * Julian Day instance or the Julian Day given as a float
15963  * 
15964  * <li><i>cycle</i> - any integer giving the number of 60-year cycle in which the date is located.
15965  * If the cycle is not given but the year is, it is assumed that the year parameter is a fictitious 
15966  * linear count of years since the beginning of the epoch, much like other calendars. This linear
15967  * count is never used. If both the cycle and year are given, the year is wrapped to the range 0 
15968  * to 60 and treated as if it were a year in the regular 60-year cycle.
15969  * 
15970  * <li><i>year</i> - any integer, including 0
15971  * 
15972  * <li><i>month</i> - 1 to 12, where 1 means Farvardin, 2 means Ordibehesht, etc.
15973  * 
15974  * <li><i>day</i> - 1 to 31
15975  * 
15976  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
15977  * is always done with an unambiguous 24 hour representation
15978  * 
15979  * <li><i>minute</i> - 0 to 59
15980  * 
15981  * <li><i>second</i> - 0 to 59
15982  * 
15983  * <li><i>millisecond</i> - 0 to 999
15984  * 
15985  * <li><i>timezone</i> - the TimeZone instance or time zone name as a string 
15986  * of this han date. The date/time is kept in the local time. The time zone
15987  * is used later if this date is formatted according to a different time zone and
15988  * the difference has to be calculated, or when the date format has a time zone
15989  * component in it.
15990  * 
15991  * <li><i>locale</i> - locale for this han date. If the time zone is not 
15992  * given, it can be inferred from this locale. For locales that span multiple
15993  * time zones, the one with the largest population is chosen as the one that 
15994  * represents the locale.
15995  * 
15996  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
15997  * </ul>
15998  *
15999  * If the constructor is called with another Han date instance instead of
16000  * a parameter block, the other instance acts as a parameter block and its
16001  * settings are copied into the current instance.<p>
16002  * 
16003  * If the constructor is called with no arguments at all or if none of the 
16004  * properties listed above 
16005  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
16006  * components are 
16007  * filled in with the current date at the time of instantiation. Note that if
16008  * you do not give the time zone when defaulting to the current time and the 
16009  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
16010  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
16011  * Mean Time").<p>
16012  * 
16013  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
16014  * specified in the params, it is assumed that they have the smallest possible
16015  * value in the range for the property (zero or one).<p>
16016  * 
16017  * 
16018  * @constructor
16019  * @extends Date
16020  * @param {Object=} params parameters that govern the settings and behaviour of this Han date
16021  */
16022 var HanDate = function(params) {
16023 	params = params || {};
16024 	if (params.locale) {
16025 		this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
16026 	}
16027 	if (params.timezone) {
16028 		this.timezone = params.timezone;
16029 	}
16030 	
16031     if (!this.timezone) {
16032         if (this.locale) {
16033             new LocaleInfo(this.locale, {
16034                 sync: params.sync,
16035                 loadParams: params.loadParams,
16036                 onLoad: ilib.bind(this, function(li) {
16037                     this.li = li;
16038                     this.timezone = li.getTimeZone();
16039                     this._init(params);
16040                 })
16041             });
16042         } else {
16043             this.timezone = "local";
16044             this._init(params);
16045         }
16046     } else {
16047         this._init(params);
16048     }
16049 };
16050 
16051 HanDate.prototype = new IDate({noinstance: true});
16052 HanDate.prototype.parent = IDate;
16053 HanDate.prototype.constructor = HanDate;
16054 
16055 /**
16056  * Initialize the han date
16057  * @private
16058  */
16059 HanDate.prototype._init = function (params) {
16060     new HanCal({
16061         sync: params && typeof(params.sync) === 'boolean' ? params.sync : true,
16062         loadParams: params && params.loadParams,
16063         onLoad: ilib.bind(this, function (cal) {
16064             this.cal = cal;
16065     
16066             if (params.year || params.month || params.day || params.hour ||
16067                 params.minute || params.second || params.millisecond || params.cycle || params.cycleYear) {
16068                 if (typeof(params.cycle) !== 'undefined') {
16069                     /**
16070                      * Cycle number in the Han calendar.
16071                      * @type number
16072                      */
16073                     this.cycle = parseInt(params.cycle, 10) || 0;
16074                     
16075                     var year = (typeof(params.year) !== 'undefined' ? parseInt(params.year, 10) : parseInt(params.cycleYear, 10)) || 0;
16076                     
16077                     /**
16078                      * Year in the Han calendar.
16079                      * @type number
16080                      */
16081                     this.year = HanCal._getElapsedYear(year, this.cycle);
16082                 } else {
16083                     if (typeof(params.year) !== 'undefined') {
16084                         this.year = parseInt(params.year, 10) || 0;
16085                         this.cycle = Math.floor((this.year - 1) / 60);
16086                     } else {
16087                         this.year = this.cycle = 0;
16088                     }
16089                 }   
16090                 
16091                 /**
16092                  * The month number, ranging from 1 to 13
16093                  * @type number
16094                  */
16095                 this.month = parseInt(params.month, 10) || 1;
16096     
16097                 /**
16098                  * The day of the month. This ranges from 1 to 30.
16099                  * @type number
16100                  */
16101                 this.day = parseInt(params.day, 10) || 1;
16102                 
16103                 /**
16104                  * The hour of the day. This can be a number from 0 to 23, as times are
16105                  * stored unambiguously in the 24-hour clock.
16106                  * @type number
16107                  */
16108                 this.hour = parseInt(params.hour, 10) || 0;
16109     
16110                 /**
16111                  * The minute of the hours. Ranges from 0 to 59.
16112                  * @type number
16113                  */
16114                 this.minute = parseInt(params.minute, 10) || 0;
16115     
16116                 /**
16117                  * The second of the minute. Ranges from 0 to 59.
16118                  * @type number
16119                  */
16120                 this.second = parseInt(params.second, 10) || 0;
16121     
16122                 /**
16123                  * The millisecond of the second. Ranges from 0 to 999.
16124                  * @type number
16125                  */
16126                 this.millisecond = parseInt(params.millisecond, 10) || 0;
16127             
16128                 // derived properties
16129                 
16130                 /**
16131                  * Year in the cycle of the Han calendar
16132                  * @type number
16133                  */
16134                 this.cycleYear = MathUtils.amod(this.year, 60); 
16135 
16136                 /**
16137                  * The day of the year. Ranges from 1 to 384.
16138                  * @type number
16139                  */
16140                 this.dayOfYear = parseInt(params.dayOfYear, 10);
16141     
16142                 if (typeof(params.dst) === 'boolean') {
16143                     this.dst = params.dst;
16144                 }
16145                 
16146                 this.newRd({
16147                     cal: this.cal,
16148                     cycle: this.cycle,
16149                     year: this.year,
16150                     month: this.month,
16151                     day: this.day,
16152                     hour: this.hour,
16153                     minute: this.minute,
16154                     second: this.second,
16155                     millisecond: this.millisecond,
16156                     sync: params.sync,
16157                     loadParams: params.loadParams,
16158                     callback: ilib.bind(this, function (rd) {
16159                         if (rd) {
16160                             this.rd = rd;
16161                             
16162                             // add the time zone offset to the rd to convert to UTC
16163                             new TimeZone({
16164                                 id: this.timezone,
16165                                 sync: params.sync,
16166                                 loadParams: params.loadParams,
16167                                 onLoad: ilib.bind(this, function(tz) {
16168                                     this.tz = tz;
16169                                     // getOffsetMillis requires that this.year, this.rd, and this.dst 
16170                                     // are set in order to figure out which time zone rules apply and 
16171                                     // what the offset is at that point in the year
16172                                     this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
16173                                     if (this.offset !== 0) {
16174                                         // this newRd can be called synchronously because we already called
16175                                         // it asynchronously above, so all of the astro data should 
16176                                         // already be loaded.
16177                                         this.rd = this.newRd({
16178                                             cal: this.cal,
16179                                             rd: this.rd.getRataDie() - this.offset
16180                                         });
16181                                         this._calcLeap();
16182                                     } else {
16183                                         // re-use the derived properties from the RD calculations
16184                                         this.leapMonth = this.rd.leapMonth;
16185                                         this.priorLeapMonth = this.rd.priorLeapMonth;
16186                                         this.leapYear = this.rd.leapYear;
16187                                     }
16188                                     
16189                                     this._init2(params);
16190                                 })
16191                             });
16192                         } else {
16193                             this._init2(params);
16194                         }
16195                     })
16196                 });
16197             } else {
16198                 this._init2(params);
16199             }
16200         })
16201     });
16202 };
16203 
16204 /**
16205  * Finish the initialization for the han date.
16206  * @private
16207  */
16208 HanDate.prototype._init2 = function (params) {
16209     if (!this.rd) {
16210         // init2() may be called without newRd having been called before,
16211         // so we cannot guarantee that the astro data is already loaded.
16212         // That means, we have to treat this as a possibly asynchronous
16213         // call.
16214         this.newRd(JSUtils.merge(params || {}, {
16215             cal: this.cal,
16216             sync: params.sync,
16217             loadParams: params.loadParams,
16218             callback: ilib.bind(this, function(rd) {
16219                 this.rd = rd;
16220                 this._calcDateComponents();
16221 
16222                 if (params && typeof(params.onLoad) === 'function') {
16223                     params.onLoad(this);
16224                 }
16225             })
16226         }));
16227     } else {
16228         if (params && typeof(params.onLoad) === 'function') {
16229             params.onLoad(this);
16230         }
16231     }
16232 };
16233 
16234 /**
16235  * Return a new RD for this date type using the given params.
16236  * @protected
16237  * @param {Object=} params the parameters used to create this rata die instance
16238  * @returns {RataDie} the new RD instance for the given params
16239  */
16240 HanDate.prototype.newRd = function (params) {
16241 	return new HanRataDie(params);
16242 };
16243 
16244 /**
16245  * Return the year for the given RD
16246  * @protected
16247  * @param {number} rd RD to calculate from 
16248  * @returns {number} the year for the RD
16249  */
16250 HanDate.prototype._calcYear = function(rd) {
16251 	var gregdate = new GregorianDate({
16252 		rd: rd,
16253 		timezone: this.timezone
16254 	});
16255 	var hanyear = gregdate.year + 2697;
16256 	var newYears = this.cal.newYears(hanyear);
16257 	return hanyear - ((rd + RataDie.gregorianEpoch < newYears) ? 1 : 0);
16258 };
16259 
16260 /** 
16261  * @private 
16262  * Calculate the leap year and months from the RD.
16263  */
16264 HanDate.prototype._calcLeap = function() {
16265 	var jd = this.rd.getRataDie() + RataDie.gregorianEpoch;
16266 	
16267 	var calc = HanCal._leapYearCalc(this.year);
16268 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
16269 	this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
16270 	
16271 	var newYears = (this.leapYear &&
16272 		(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
16273 				HanCal._newMoonOnOrAfter(m2+1) : m2;
16274 	
16275 	var m = HanCal._newMoonBefore(jd + 1);
16276 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(m));
16277 	this.leapMonth = (this.leapYear && HanCal._noMajorST(m) && !this.priorLeapMonth);
16278 };
16279 
16280 /**
16281  * @private
16282  * Calculate date components for the given RD date.
16283  */
16284 HanDate.prototype._calcDateComponents = function () {
16285 	var remainder,
16286 		jd = this.rd.getRataDie() + RataDie.gregorianEpoch;
16287 
16288 	// console.log("HanDate._calcDateComponents: calculating for jd " + jd);
16289 
16290 	if (typeof(this.offset) === "undefined") {
16291 		// now offset the jd by the time zone, then recalculate in case we were 
16292 		// near the year boundary
16293 		if (!this.tz) {
16294 			this.tz = new TimeZone({id: this.timezone});
16295 		}
16296 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
16297 	}
16298 	
16299 	if (this.offset !== 0) {
16300 		jd += this.offset;
16301 	}
16302 
16303 	// use the Gregorian calendar objects as a convenient way to short-cut some
16304 	// of the date calculations
16305 	
16306 	var gregyear = GregorianDate._calcYear(this.rd.getRataDie());
16307 	this.year = gregyear + 2697;
16308 	var calc = HanCal._leapYearCalc(this.year);
16309 	var m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
16310 	this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
16311 	var newYears = (this.leapYear &&
16312 		(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
16313 				HanCal._newMoonOnOrAfter(m2+1) : m2;
16314 	
16315 	// See if it's between Jan 1 and the Chinese new years of that Gregorian year. If
16316 	// so, then the Han year is actually the previous one
16317 	if (jd < newYears) {
16318 		this.year--;
16319 		calc = HanCal._leapYearCalc(this.year);
16320 		m2 = HanCal._newMoonOnOrAfter(calc.m1+1);
16321 		this.leapYear = Math.round((calc.m2 - calc.m1) / 29.530588853000001) === 12;
16322 		newYears = (this.leapYear &&
16323 			(HanCal._noMajorST(calc.m1) || HanCal._noMajorST(m2))) ?
16324 					HanCal._newMoonOnOrAfter(m2+1) : m2;
16325 	}
16326 	// month is elapsed month, not the month number + leap month boolean
16327 	var m = HanCal._newMoonBefore(jd + 1);
16328 	this.month = Math.round((m - calc.m1) / 29.530588853000001);
16329 	
16330 	this.priorLeapMonth = HanRataDie._priorLeapMonth(newYears, HanCal._newMoonBefore(m));
16331 	this.leapMonth = (this.leapYear && HanCal._noMajorST(m) && !this.priorLeapMonth);
16332 	
16333 	this.cycle = Math.floor((this.year - 1) / 60);
16334 	this.cycleYear = MathUtils.amod(this.year, 60);
16335 	this.day = Astro._floorToJD(jd) - m + 1;
16336 
16337 	/*
16338 	console.log("HanDate._calcDateComponents: year is " + this.year);
16339 	console.log("HanDate._calcDateComponents: isLeapYear is " + this.leapYear);
16340 	console.log("HanDate._calcDateComponents: cycle is " + this.cycle);
16341 	console.log("HanDate._calcDateComponents: cycleYear is " + this.cycleYear);
16342 	console.log("HanDate._calcDateComponents: month is " + this.month);
16343 	console.log("HanDate._calcDateComponents: isLeapMonth is " + this.leapMonth);
16344 	console.log("HanDate._calcDateComponents: day is " + this.day);
16345 	*/
16346 
16347 	// floor to the start of the julian day
16348 	remainder = jd - Astro._floorToJD(jd);
16349 	
16350 	// console.log("HanDate._calcDateComponents: time remainder is " + remainder);
16351 	
16352 	// now convert to milliseconds for the rest of the calculation
16353 	remainder = Math.round(remainder * 86400000);
16354 	
16355 	this.hour = Math.floor(remainder/3600000);
16356 	remainder -= this.hour * 3600000;
16357 	
16358 	this.minute = Math.floor(remainder/60000);
16359 	remainder -= this.minute * 60000;
16360 	
16361 	this.second = Math.floor(remainder/1000);
16362 	remainder -= this.second * 1000;
16363 	
16364 	this.millisecond = remainder;
16365 };
16366 
16367 /**
16368  * Return the year within the Chinese cycle of this date. Cycles are 60 
16369  * years long, and the value returned from this method is the number of the year 
16370  * within this cycle. The year returned from getYear() is the total elapsed 
16371  * years since the beginning of the Chinese epoch and does not include 
16372  * the cycles. 
16373  * 
16374  * @return {number} the year within the current Chinese cycle
16375  */
16376 HanDate.prototype.getCycleYears = function() {
16377 	return this.cycleYear;
16378 };
16379 
16380 /**
16381  * Return the Chinese cycle number of this date. Cycles are 60 years long,
16382  * and the value returned from getCycleYear() is the number of the year 
16383  * within this cycle. The year returned from getYear() is the total elapsed 
16384  * years since the beginning of the Chinese epoch and does not include 
16385  * the cycles. 
16386  * 
16387  * @return {number} the current Chinese cycle
16388  */
16389 HanDate.prototype.getCycles = function() {
16390 	return this.cycle;
16391 };
16392 
16393 /**
16394  * Return whether the year of this date is a leap year in the Chinese Han 
16395  * calendar. 
16396  * 
16397  * @return {boolean} true if the year of this date is a leap year in the 
16398  * Chinese Han calendar. 
16399  */
16400 HanDate.prototype.isLeapYear = function() {
16401 	return this.leapYear;
16402 };
16403 
16404 /**
16405  * Return whether the month of this date is a leap month in the Chinese Han 
16406  * calendar.
16407  * 
16408  * @return {boolean} true if the month of this date is a leap month in the 
16409  * Chinese Han calendar.
16410  */
16411 HanDate.prototype.isLeapMonth = function() {
16412 	return this.leapMonth;
16413 };
16414 
16415 /**
16416  * Return the day of the week of this date. The day of the week is encoded
16417  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
16418  * 
16419  * @return {number} the day of the week
16420  */
16421 HanDate.prototype.getDayOfWeek = function() {
16422 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
16423 	return MathUtils.mod(rd, 7);
16424 };
16425 
16426 /**
16427  * Return the ordinal day of the year. Days are counted from 1 and proceed linearly up to 
16428  * 365, regardless of months or weeks, etc. That is, Farvardin 1st is day 1, and 
16429  * December 31st is 365 in regular years, or 366 in leap years.
16430  * @return {number} the ordinal day of the year
16431  */
16432 HanDate.prototype.getDayOfYear = function() {
16433 	var newYears = this.cal.newYears(this.year);
16434 	var priorNewMoon = HanCal._newMoonOnOrAfter(newYears + (this.month -1) * 29);
16435 	return priorNewMoon - newYears + this.day;
16436 };
16437 
16438 /**
16439  * Return the era for this date as a number. The value for the era for Han 
16440  * calendars is -1 for "before the han era" (BP) and 1 for "the han era" (anno 
16441  * persico or AP). 
16442  * BP dates are any date before Farvardin 1, 1 AP. In the proleptic Han calendar, 
16443  * there is a year 0, so any years that are negative or zero are BP.
16444  * @return {number} 1 if this date is in the common era, -1 if it is before the 
16445  * common era 
16446  */
16447 HanDate.prototype.getEra = function() {
16448 	return (this.year < 1) ? -1 : 1;
16449 };
16450 
16451 /**
16452  * Return the name of the calendar that governs this date.
16453  * 
16454  * @return {string} a string giving the name of the calendar
16455  */
16456 HanDate.prototype.getCalendar = function() {
16457 	return "han";
16458 };
16459 
16460 // register with the factory method
16461 IDate._constructors["han"] = HanDate;
16462 
16463 
16464 /*< EthiopicCal.js */
16465 /*
16466  * ethiopic.js - Represent a Ethiopic calendar object.
16467  * 
16468  * Copyright © 2015,2018, JEDLSoft
16469  *
16470  * Licensed under the Apache License, Version 2.0 (the "License");
16471  * you may not use this file except in compliance with the License.
16472  * You may obtain a copy of the License at
16473  *
16474  *     http://www.apache.org/licenses/LICENSE-2.0
16475  *
16476  * Unless required by applicable law or agreed to in writing, software
16477  * distributed under the License is distributed on an "AS IS" BASIS,
16478  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16479  *
16480  * See the License for the specific language governing permissions and
16481  * limitations under the License.
16482  */
16483 
16484 /* !depends Calendar.js MathUtils.js */
16485 
16486 
16487 
16488 /**
16489  * @class
16490  * Construct a new Ethiopic calendar object. This class encodes information about
16491  * a Ethiopic calendar.<p>
16492  * 
16493  * @param {Object=} options Options governing the construction of this instance
16494  * @constructor
16495  * @extends Calendar
16496  */
16497 var EthiopicCal = function(options) {
16498 	this.type = "ethiopic";
16499     
16500     if (options && typeof(options.onLoad) === "function") {
16501         options.onLoad(this);
16502     }
16503 };
16504 
16505 /**
16506  * Return the number of months in the given year. The number of months in a year varies
16507  * for lunar calendars because in some years, an extra month is needed to extend the 
16508  * days in a year to an entire solar year. The month is represented as a 1-based number
16509  * where 1=Maskaram, 2=Teqemt, etc. until 13=Paguemen.
16510  * 
16511  * @param {number} year a year for which the number of months is sought
16512  */
16513 EthiopicCal.prototype.getNumMonths = function(year) {
16514 	return 13;
16515 };
16516 
16517 /**
16518  * Return the number of days in a particular month in a particular year. This function
16519  * can return a different number for a month depending on the year because of things
16520  * like leap years.
16521  * 
16522  * @param {number|string} month the month for which the length is sought
16523  * @param {number} year the year within which that month can be found
16524  * @return {number} the number of days within the given month in the given year
16525  */
16526 EthiopicCal.prototype.getMonLength = function(month, year) {
16527 	var m = month;
16528 	switch (typeof(m)) {
16529         case "string": 
16530             m = parseInt(m, 10); 
16531             break;
16532         case "function":
16533         case "object":
16534         case "undefined":
16535             return 30;
16536     }    
16537 	if (m < 13) {
16538 		return 30;
16539 	} else {
16540 		return this.isLeapYear(year) ? 6 : 5;
16541 	}
16542 };
16543 
16544 /**
16545  * Return true if the given year is a leap year in the Ethiopic calendar.
16546  * The year parameter may be given as a number, or as a JulDate object.
16547  * @param {number|EthiopicDate|string} year the year for which the leap year information is being sought
16548  * @return {boolean} true if the given year is a leap year
16549  */
16550 EthiopicCal.prototype.isLeapYear = function(year) {
16551 	var y = year;
16552 	 switch (typeof(y)) {
16553         case "string":
16554             y = parseInt(y, 10);
16555             break;
16556         case "object":
16557             if (typeof(y.year) !== "number") { // in case it is an IDate object
16558                 return false;
16559             }
16560             y = y.year;
16561             break;
16562         case "function":
16563         case "undefined":
16564             return false;
16565             break;
16566     }
16567 	return MathUtils.mod(y, 4) === 3;
16568 };
16569 
16570 /**
16571  * Return the type of this calendar.
16572  * 
16573  * @return {string} the name of the type of this calendar 
16574  */
16575 EthiopicCal.prototype.getType = function() {
16576 	return this.type;
16577 };
16578 
16579 
16580 /* register this calendar for the factory method */
16581 Calendar._constructors["ethiopic"] = EthiopicCal;
16582 
16583 
16584 
16585 /*< EthiopicRataDie.js */
16586 /*
16587  * EthiopicRataDie.js - Represent an RD date in the Ethiopic calendar
16588  * 
16589  * Copyright © 2015, JEDLSoft
16590  *
16591  * Licensed under the Apache License, Version 2.0 (the "License");
16592  * you may not use this file except in compliance with the License.
16593  * You may obtain a copy of the License at
16594  *
16595  *     http://www.apache.org/licenses/LICENSE-2.0
16596  *
16597  * Unless required by applicable law or agreed to in writing, software
16598  * distributed under the License is distributed on an "AS IS" BASIS,
16599  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16600  *
16601  * See the License for the specific language governing permissions and
16602  * limitations under the License.
16603  */
16604 
16605 /* !depends 
16606 EthiopicCal.js
16607 RataDie.js
16608 */
16609 
16610 
16611 /**
16612  * @class
16613  * Construct a new Ethiopic RD date number object. The constructor parameters can 
16614  * contain any of the following properties:
16615  * 
16616  * <ul>
16617  * <li><i>unixtime<i> - sets the time of this instance according to the given 
16618  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
16619  * 
16620  * <li><i>julianday</i> - sets the time of this instance according to the given
16621  * Julian Day instance or the Julian Day given as a float
16622  * 
16623  * <li><i>year</i> - any integer, including 0
16624  * 
16625  * <li><i>month</i> - 1 to 12, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen
16626  * 
16627  * <li><i>day</i> - 1 to 30
16628  * 
16629  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
16630  * is always done with an unambiguous 24 hour representation
16631  * 
16632  * <li><i>minute</i> - 0 to 59
16633  * 
16634  * <li><i>second</i> - 0 to 59
16635  * 
16636  * <li><i>millisecond</i> - 0 to 999
16637  * 
16638  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16639  * </ul>
16640  *
16641  * If the constructor is called with another Ethiopic date instance instead of
16642  * a parameter block, the other instance acts as a parameter block and its
16643  * settings are copied into the current instance.<p>
16644  * 
16645  * If the constructor is called with no arguments at all or if none of the 
16646  * properties listed above are present, then the RD is calculate based on 
16647  * the current date at the time of instantiation. <p>
16648  * 
16649  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
16650  * specified in the params, it is assumed that they have the smallest possible
16651  * value in the range for the property (zero or one).<p>
16652  * 
16653  * 
16654  * @private
16655  * @constructor
16656  * @extends RataDie
16657  * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic RD date
16658  */
16659 var EthiopicRataDie = function(params) {
16660 	this.cal = params && params.cal || new EthiopicCal();
16661 	this.rd = NaN;
16662 	RataDie.call(this, params);
16663 };
16664 
16665 EthiopicRataDie.prototype = new RataDie();
16666 EthiopicRataDie.prototype.parent = RataDie;
16667 EthiopicRataDie.prototype.constructor = EthiopicRataDie;
16668 
16669 /**
16670  * The difference between the zero Julian day and the first Ethiopic date
16671  * of Friday, August 29, 8 CE Julian at 6:00am UTC.<p> 
16672  * 
16673  * See <a href="http://us.wow.com/wiki/Time_in_Ethiopia?s_chn=90&s_pt=aolsem&v_t=aolsem"
16674  * Time in Ethiopia</a> for information about how time is handled in Ethiopia.
16675  * 
16676  * @protected
16677  * @type number
16678  */
16679 EthiopicRataDie.prototype.epoch = 1724219.75;
16680 
16681 /**
16682  * Calculate the Rata Die (fixed day) number of the given date from the
16683  * date components.
16684  * 
16685  * @protected
16686  * @param {Object} date the date components to calculate the RD from
16687  */
16688 EthiopicRataDie.prototype._setDateComponents = function(date) {
16689 	var year = date.year;
16690 	var years = 365 * (year - 1) + Math.floor(year/4);
16691 	var dayInYear = (date.month-1) * 30 + date.day;
16692 	var rdtime = (date.hour * 3600000 +
16693 		date.minute * 60000 +
16694 		date.second * 1000 +
16695 		date.millisecond) / 
16696 		86400000;
16697 	
16698 	/*
16699 	console.log("calcRataDie: converting " +  JSON.stringify(parts));
16700 	console.log("getRataDie: year is " +  years);
16701 	console.log("getRataDie: day in year is " +  dayInYear);
16702 	console.log("getRataDie: rdtime is " +  rdtime);
16703 	console.log("getRataDie: rd is " +  (years + dayInYear + rdtime));
16704 	*/
16705 	
16706 	this.rd = years + dayInYear + rdtime;
16707 };
16708 
16709 
16710 
16711 /*< EthiopicDate.js */
16712 /*
16713  * EthiopicDate.js - Represent a date in the Ethiopic calendar
16714  *
16715  * Copyright © 2015, 2018, JEDLSoft
16716  *
16717  * Licensed under the Apache License, Version 2.0 (the "License");
16718  * you may not use this file except in compliance with the License.
16719  * You may obtain a copy of the License at
16720  *
16721  *     http://www.apache.org/licenses/LICENSE-2.0
16722  *
16723  * Unless required by applicable law or agreed to in writing, software
16724  * distributed under the License is distributed on an "AS IS" BASIS,
16725  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16726  *
16727  * See the License for the specific language governing permissions and
16728  * limitations under the License.
16729  */
16730 
16731 /* !depends 
16732 ilib.js
16733 IDate.js 
16734 EthiopicCal.js 
16735 MathUtils.js
16736 Locale.js
16737 LocaleInfo.js 
16738 TimeZone.js
16739 EthiopicRataDie.js
16740 */
16741 
16742 
16743 
16744 /**
16745  * @class
16746  * Construct a new date object for the Ethiopic Calendar. The constructor can be called
16747  * with a parameter object that contains any of the following properties:
16748  *
16749  * <ul>
16750  * <li><i>unixtime<i> - sets the time of this instance according to the given
16751  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
16752  * <li><i>julianday</i> - the Julian Day to set into this date
16753  * <li><i>year</i> - any integer
16754  * <li><i>month</i> - 1 to 13, where 1 means Maskaram, 2 means Teqemt, etc., and 13 means Paguemen
16755  * <li><i>day</i> - 1 to 30
16756  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation
16757  * is always done with an unambiguous 24 hour representation
16758  * <li><i>minute</i> - 0 to 59
16759  * <li><i>second</i> - 0 to 59
16760  * <li><i>millisecond<i> - 0 to 999
16761  * <li><i>locale</i> - the TimeZone instance or time zone name as a string
16762  * of this ethiopic date. The date/time is kept in the local time. The time zone
16763  * is used later if this date is formatted according to a different time zone and
16764  * the difference has to be calculated, or when the date format has a time zone
16765  * component in it.
16766  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not
16767  * given, it can be inferred from this locale. For locales that span multiple
16768  * time zones, the one with the largest population is chosen as the one that
16769  * represents the locale.
16770  *
16771  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
16772  * </ul>
16773  *
16774  * If called with another Ethiopic date argument, the date components of the given
16775  * date are copied into the current one.<p>
16776  *
16777  * If the constructor is called with no arguments at all or if none of the
16778  * properties listed above
16779  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date
16780  * components are
16781  * filled in with the current date at the time of instantiation. Note that if
16782  * you do not give the time zone when defaulting to the current time and the
16783  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
16784  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich
16785  * Mean Time").<p>
16786  *
16787  *
16788  * @constructor
16789  * @extends IDate
16790  * @param {Object=} params parameters that govern the settings and behaviour of this Ethiopic date
16791  */
16792 var EthiopicDate = function(params) {
16793     this.cal = new EthiopicCal();
16794 
16795     params = params || {};
16796 
16797     if (typeof(params.noinstance) === 'boolean' && params.noinstance) {
16798         // for doing inheritance, so don't need to fill in the data. The inheriting class only wants the methods.
16799         return;
16800     }
16801     if (params.timezone) {
16802         this.timezone = params.timezone;
16803     }
16804     if (params.locale) {
16805         this.locale = (typeof(params.locale) === 'string') ? new Locale(params.locale) : params.locale;
16806     }
16807 
16808     if (!this.timezone) {
16809         if (this.locale) {
16810             new LocaleInfo(this.locale, {
16811                 sync: params.sync,
16812                 loadParams: params.loadParams,
16813                 onLoad: ilib.bind(this, function(li) {
16814                     this.li = li;
16815                     this.timezone = li.getTimeZone();
16816                     this._init(params);
16817                 })
16818             });
16819         } else {
16820             this.timezone = "local";
16821             this._init(params);
16822         }
16823     } else {
16824         this._init(params);
16825     }
16826 };
16827 
16828 EthiopicDate.prototype = new IDate({ noinstance: true });
16829 EthiopicDate.prototype.parent = IDate;
16830 EthiopicDate.prototype.constructor = EthiopicDate;
16831 
16832 /**
16833  * Initialize this instance
16834  * @private
16835  */
16836 EthiopicDate.prototype._init = function (params) {
16837     new TimeZone({
16838         id: this.timezone,
16839         sync: params.sync,
16840         loadParams: params.loadParams,
16841         onLoad: ilib.bind(this, function(tz) {
16842             this.tz = tz;
16843 
16844             if (params.year || params.month || params.day || params.hour ||
16845                 params.minute || params.second || params.millisecond ) {
16846                 /**
16847                  * Year in the Ethiopic calendar.
16848                  * @type number
16849                  */
16850                 this.year = parseInt(params.year, 10) || 0;
16851                 /**
16852                  * The month number, ranging from 1 (Maskaram) to 13 (Paguemen).
16853                  * @type number
16854                  */
16855                 this.month = parseInt(params.month, 10) || 1;
16856                 /**
16857                  * The day of the month. This ranges from 1 to 30.
16858                  * @type number
16859                  */
16860                 this.day = parseInt(params.day, 10) || 1;
16861                 /**
16862                  * The hour of the day. This can be a number from 0 to 23, as times are
16863                  * stored unambiguously in the 24-hour clock.
16864                  * @type number
16865                  */
16866                 this.hour = parseInt(params.hour, 10) || 0;
16867                 /**
16868                  * The minute of the hours. Ranges from 0 to 59.
16869                  * @type number
16870                  */
16871                 this.minute = parseInt(params.minute, 10) || 0;
16872                 /**
16873                  * The second of the minute. Ranges from 0 to 59.
16874                  * @type number
16875                  */
16876                 this.second = parseInt(params.second, 10) || 0;
16877                 /**
16878                  * The millisecond of the second. Ranges from 0 to 999.
16879                  * @type number
16880                  */
16881                 this.millisecond = parseInt(params.millisecond, 10) || 0;
16882 
16883                 /**
16884                  * The day of the year. Ranges from 1 to 366.
16885                  * @type number
16886                  */
16887                 this.dayOfYear = parseInt(params.dayOfYear, 10);
16888 
16889                 if (typeof(params.dst) === 'boolean') {
16890                     this.dst = params.dst;
16891                 }
16892 
16893                 this.rd = this.newRd(this);
16894 
16895                 // add the time zone offset to the rd to convert to UTC
16896                 // getOffsetMillis requires that this.year, this.rd, and this.dst
16897                 // are set in order to figure out which time zone rules apply and
16898                 // what the offset is at that point in the year
16899                 this.offset = this.tz._getOffsetMillisWallTime(this) / 86400000;
16900                 if (this.offset !== 0) {
16901                     this.rd = this.newRd({
16902                         rd: this.rd.getRataDie() - this.offset
16903                     });
16904                 }
16905             }
16906 
16907             if (!this.rd) {
16908                 this.rd = this.newRd(params);
16909                 this._calcDateComponents();
16910             }
16911 
16912             if (typeof(params.onLoad) === "function") {
16913                 params.onLoad(this);
16914             }
16915         })
16916     });
16917 };
16918 
16919 /**
16920  * Return a new RD for this date type using the given params.
16921  * @protected
16922  * @param {Object=} params the parameters used to create this rata die instance
16923  * @returns {RataDie} the new RD instance for the given params
16924  */
16925 EthiopicDate.prototype.newRd = function (params) {
16926 	return new EthiopicRataDie(params);
16927 };
16928 
16929 /**
16930  * Return the year for the given RD
16931  * @protected
16932  * @param {number} rd RD to calculate from
16933  * @returns {number} the year for the RD
16934  */
16935 EthiopicDate.prototype._calcYear = function(rd) {
16936 	var year = Math.floor((4*(Math.floor(rd)-1) + 1463)/1461);
16937 
16938 	return year;
16939 };
16940 
16941 /**
16942  * Calculate date components for the given RD date.
16943  * @protected
16944  */
16945 EthiopicDate.prototype._calcDateComponents = function () {
16946 	var remainder,
16947 		rd = this.rd.getRataDie();
16948 
16949 	this.year = this._calcYear(rd);
16950 
16951 	if (typeof(this.offset) === "undefined") {
16952 		this.year = this._calcYear(rd);
16953 
16954 		// now offset the RD by the time zone, then recalculate in case we were
16955 		// near the year boundary
16956 		if (!this.tz) {
16957 			this.tz = new TimeZone({id: this.timezone});
16958 		}
16959 		this.offset = this.tz.getOffsetMillis(this) / 86400000;
16960 	}
16961 
16962 	if (this.offset !== 0) {
16963 		rd += this.offset;
16964 		this.year = this._calcYear(rd);
16965 	}
16966 
16967 	var jan1 = this.newRd({
16968 		year: this.year,
16969 		month: 1,
16970 		day: 1,
16971 		hour: 0,
16972 		minute: 0,
16973 		second: 0,
16974 		millisecond: 0
16975 	});
16976 	remainder = rd + 1 - jan1.getRataDie();
16977 
16978 	this.month = Math.floor((remainder-1)/30) + 1;
16979 	remainder = remainder - (this.month-1) * 30;
16980 
16981 	this.day = Math.floor(remainder);
16982 	remainder -= this.day;
16983 	// now convert to milliseconds for the rest of the calculation
16984 	remainder = Math.round(remainder * 86400000);
16985 
16986 	this.hour = Math.floor(remainder/3600000);
16987 	remainder -= this.hour * 3600000;
16988 
16989 	this.minute = Math.floor(remainder/60000);
16990 	remainder -= this.minute * 60000;
16991 
16992 	this.second = Math.floor(remainder/1000);
16993 	remainder -= this.second * 1000;
16994 
16995 	this.millisecond = remainder;
16996 };
16997 
16998 /**
16999  * Return the day of the week of this date. The day of the week is encoded
17000  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
17001  *
17002  * @return {number} the day of the week
17003  */
17004 EthiopicDate.prototype.getDayOfWeek = function() {
17005 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
17006 	return MathUtils.mod(rd-4, 7);
17007 };
17008 
17009 /**
17010  * Return the name of the calendar that governs this date.
17011  *
17012  * @return {string} a string giving the name of the calendar
17013  */
17014 EthiopicDate.prototype.getCalendar = function() {
17015 	return "ethiopic";
17016 };
17017 
17018 //register with the factory method
17019 IDate._constructors["ethiopic"] = EthiopicDate;
17020 
17021 
17022 
17023 /*< CopticCal.js */
17024 /*
17025  * CopticCal.js - Represent a Coptic calendar object.
17026  * 
17027  * Copyright © 2015,2018, JEDLSoft
17028  *
17029  * Licensed under the Apache License, Version 2.0 (the "License");
17030  * you may not use this file except in compliance with the License.
17031  * You may obtain a copy of the License at
17032  *
17033  *     http://www.apache.org/licenses/LICENSE-2.0
17034  *
17035  * Unless required by applicable law or agreed to in writing, software
17036  * distributed under the License is distributed on an "AS IS" BASIS,
17037  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17038  *
17039  * See the License for the specific language governing permissions and
17040  * limitations under the License.
17041  */
17042 
17043 
17044 /* !depends Calendar.js Utils.js EthiopicCal.js */
17045 
17046 
17047 /**
17048  * @class
17049  * Construct a new Coptic calendar object. This class encodes information about
17050  * a Coptic calendar.<p>
17051  * 
17052  * @param {Object=} options Options governing the construction of this instance
17053  * @constructor
17054  * @extends EthiopicCal
17055  */
17056 var CopticCal = function(options) {
17057 	this.type = "coptic";
17058     
17059     if (options && typeof(options.onLoad) === "function") {
17060         options.onLoad(this);
17061     }
17062 };
17063 
17064 CopticCal.prototype = new EthiopicCal();
17065 CopticCal.prototype.parent = EthiopicCal;
17066 CopticCal.prototype.constructor = CopticCal;
17067 
17068 
17069 /* register this calendar for the factory method */
17070 Calendar._constructors["coptic"] = CopticCal;
17071 
17072 
17073 
17074 /*< CopticRataDie.js */
17075 /*
17076  * CopticRataDie.js - Represent an RD date in the Coptic calendar
17077  * 
17078  * Copyright © 2015, JEDLSoft
17079  *
17080  * Licensed under the Apache License, Version 2.0 (the "License");
17081  * you may not use this file except in compliance with the License.
17082  * You may obtain a copy of the License at
17083  *
17084  *     http://www.apache.org/licenses/LICENSE-2.0
17085  *
17086  * Unless required by applicable law or agreed to in writing, software
17087  * distributed under the License is distributed on an "AS IS" BASIS,
17088  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17089  *
17090  * See the License for the specific language governing permissions and
17091  * limitations under the License.
17092  */
17093 
17094 /* !depends 
17095 CopticCal.js 
17096 JSUtils.js
17097 EthiopicRataDie.js
17098 */
17099 
17100 
17101 /**
17102  * @class
17103  * Construct a new Coptic RD date number object. The constructor parameters can 
17104  * contain any of the following properties:
17105  * 
17106  * <ul>
17107  * <li><i>unixtime<i> - sets the time of this instance according to the given 
17108  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970.
17109  * 
17110  * <li><i>julianday</i> - sets the time of this instance according to the given
17111  * Julian Day instance or the Julian Day given as a float
17112  * 
17113  * <li><i>year</i> - any integer, including 0
17114  * 
17115  * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene
17116  * 
17117  * <li><i>day</i> - 1 to 30
17118  * 
17119  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
17120  * is always done with an unambiguous 24 hour representation
17121  * 
17122  * <li><i>minute</i> - 0 to 59
17123  * 
17124  * <li><i>second</i> - 0 to 59
17125  * 
17126  * <li><i>millisecond</i> - 0 to 999
17127  * 
17128  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
17129  * </ul>
17130  *
17131  * If the constructor is called with another Coptic date instance instead of
17132  * a parameter block, the other instance acts as a parameter block and its
17133  * settings are copied into the current instance.<p>
17134  * 
17135  * If the constructor is called with no arguments at all or if none of the 
17136  * properties listed above are present, then the RD is calculate based on 
17137  * the current date at the time of instantiation. <p>
17138  * 
17139  * If any of the properties from <i>year</i> through <i>millisecond</i> are not
17140  * specified in the params, it is assumed that they have the smallest possible
17141  * value in the range for the property (zero or one).<p>
17142  * 
17143  * 
17144  * @private
17145  * @constructor
17146  * @extends EthiopicRataDie
17147  * @param {Object=} params parameters that govern the settings and behaviour of this Coptic RD date
17148  */
17149 var CopticRataDie = function(params) {
17150 	this.cal = params && params.cal || new CopticCal();
17151 	this.rd = NaN;
17152 	/**
17153 	 * The difference between the zero Julian day and the first Coptic date
17154 	 * of Friday, August 29, 284 CE Julian at 7:00am UTC. 
17155 	 * @private
17156 	 * @type number
17157 	 */
17158 	this.epoch = 1825028.5;
17159 
17160 	var tmp = {};
17161 	if (params) {
17162 		JSUtils.shallowCopy(params, tmp);
17163 	}
17164 	tmp.cal = this.cal; // override the cal parameter that may be passed in
17165 	EthiopicRataDie.call(this, tmp);
17166 };
17167 
17168 CopticRataDie.prototype = new EthiopicRataDie();
17169 CopticRataDie.prototype.parent = EthiopicRataDie;
17170 CopticRataDie.prototype.constructor = CopticRataDie;
17171 
17172 
17173 /*< CopticDate.js */
17174 /*
17175  * CopticDate.js - Represent a date in the Coptic calendar
17176  * 
17177  * Copyright © 2015, JEDLSoft
17178  *
17179  * Licensed under the Apache License, Version 2.0 (the "License");
17180  * you may not use this file except in compliance with the License.
17181  * You may obtain a copy of the License at
17182  *
17183  *     http://www.apache.org/licenses/LICENSE-2.0
17184  *
17185  * Unless required by applicable law or agreed to in writing, software
17186  * distributed under the License is distributed on an "AS IS" BASIS,
17187  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17188  *
17189  * See the License for the specific language governing permissions and
17190  * limitations under the License.
17191  */
17192 
17193 /* !depends 
17194 ilib.js
17195 IDate.js 
17196 CopticCal.js 
17197 MathUtils.js
17198 Locale.js
17199 EthiopicDate.js
17200 CopticRataDie.js
17201 */
17202 
17203 
17204 
17205 
17206 /**
17207  * @class
17208  * Construct a new date object for the Coptic Calendar. The constructor can be called
17209  * with a parameter object that contains any of the following properties:
17210  * 
17211  * <ul>
17212  * <li><i>unixtime<i> - sets the time of this instance according to the given 
17213  * unix time. Unix time is the number of milliseconds since midnight on Jan 1, 1970 (Gregorian).
17214  * <li><i>julianday</i> - the Julian Day to set into this date
17215  * <li><i>year</i> - any integer
17216  * <li><i>month</i> - 1 to 13, where 1 means Thoout, 2 means Paope, etc., and 13 means Epagomene
17217  * <li><i>day</i> - 1 to 30
17218  * <li><i>hour</i> - 0 to 23. A formatter is used to display 12 hour clocks, but this representation 
17219  * is always done with an unambiguous 24 hour representation
17220  * <li><i>minute</i> - 0 to 59
17221  * <li><i>second</i> - 0 to 59
17222  * <li><i>millisecond<i> - 0 to 999
17223  * <li><i>locale</i> - the TimeZone instance or time zone name as a string 
17224  * of this coptic date. The date/time is kept in the local time. The time zone
17225  * is used later if this date is formatted according to a different time zone and
17226  * the difference has to be calculated, or when the date format has a time zone
17227  * component in it.
17228  * <li><i>timezone</i> - the time zone of this instance. If the time zone is not 
17229  * given, it can be inferred from this locale. For locales that span multiple
17230  * time zones, the one with the largest population is chosen as the one that 
17231  * represents the locale. 
17232  * 
17233  * <li><i>date</i> - use the given intrinsic Javascript date to initialize this one.
17234  * </ul>
17235  *  
17236  * If called with another Coptic date argument, the date components of the given
17237  * date are copied into the current one.<p>
17238  * 
17239  * If the constructor is called with no arguments at all or if none of the 
17240  * properties listed above 
17241  * from <i>unixtime</i> through <i>millisecond</i> are present, then the date 
17242  * components are 
17243  * filled in with the current date at the time of instantiation. Note that if
17244  * you do not give the time zone when defaulting to the current time and the 
17245  * time zone for all of ilib was not set with <i>ilib.setTimeZone()</i>, then the
17246  * time zone will default to UTC ("Universal Time, Coordinated" or "Greenwich 
17247  * Mean Time").<p>
17248  * 
17249  * 
17250  * @constructor
17251  * @extends EthiopicDate
17252  * @param {Object=} params parameters that govern the settings and behaviour of this Coptic date
17253  */
17254 var CopticDate = function(params) {
17255     this.rd = NaN; // clear these out so that the EthiopicDate constructor can set it
17256     var newparams = ilib.extend({}, params);
17257     newparams.onLoad = function(ed) {
17258         ed.cal = new CopticCal();
17259         if (typeof(params.onLoad) === "function") {
17260             params.onLoad(ed);
17261         }
17262     };
17263     EthiopicDate.call(this, params);
17264 };
17265 
17266 CopticDate.prototype = new EthiopicDate({noinstance: true});
17267 CopticDate.prototype.parent = EthiopicDate.prototype;
17268 CopticDate.prototype.constructor = CopticDate;
17269 
17270 /**
17271  * Return a new RD for this date type using the given params.
17272  * @protected
17273  * @param {Object=} params the parameters used to create this rata die instance
17274  * @returns {RataDie} the new RD instance for the given params
17275  */
17276 CopticDate.prototype.newRd = function (params) {
17277 	return new CopticRataDie(params);
17278 };
17279 
17280 /**
17281  * Return the day of the week of this date. The day of the week is encoded
17282  * as number from 0 to 6, with 0=Sunday, 1=Monday, etc., until 6=Saturday.
17283  * 
17284  * @return {number} the day of the week
17285  */
17286 CopticDate.prototype.getDayOfWeek = function() {
17287 	var rd = Math.floor(this.rd.getRataDie() + (this.offset || 0));
17288 	return MathUtils.mod(rd-3, 7);
17289 };
17290 
17291 /**
17292  * Return the name of the calendar that governs this date.
17293  * 
17294  * @return {string} a string giving the name of the calendar
17295  */
17296 CopticDate.prototype.getCalendar = function() {
17297 	return "coptic";
17298 };
17299 
17300 //register with the factory method
17301 IDate._constructors["coptic"] = CopticDate;
17302 
17303 
17304 /*< CType.js */
17305 /*
17306  * CType.js - Character type definitions
17307  * 
17308  * Copyright © 2012-2015, JEDLSoft
17309  *
17310  * Licensed under the Apache License, Version 2.0 (the "License");
17311  * you may not use this file except in compliance with the License.
17312  * You may obtain a copy of the License at
17313  *
17314  *     http://www.apache.org/licenses/LICENSE-2.0
17315  *
17316  * Unless required by applicable law or agreed to in writing, software
17317  * distributed under the License is distributed on an "AS IS" BASIS,
17318  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17319  *
17320  * See the License for the specific language governing permissions and
17321  * limitations under the License.
17322  */
17323 
17324 // !depends ilib.js SearchUtils.js Utils.js IString.js
17325 
17326 // !data ctype
17327 
17328 
17329 /**
17330  * Provides a set of static routines that return information about characters.
17331  * These routines emulate the C-library ctype functions. The characters must be 
17332  * encoded in utf-16, as no other charsets are currently supported. Only the first
17333  * character of the given string is tested.
17334  * @namespace
17335  */
17336 var CType = {};
17337 
17338 
17339 /**
17340  * Actual implementation for withinRange. Searches the given object for ranges.
17341  * The range names are taken from the Unicode range names in 
17342  * http://www.unicode.org/Public/UNIDATA/extracted/DerivedGeneralCategory.txt
17343  * 
17344  * <ul>
17345  * <li>Cn - Unassigned
17346  * <li>Lu - Uppercase_Letter
17347  * <li>Ll - Lowercase_Letter
17348  * <li>Lt - Titlecase_Letter
17349  * <li>Lm - Modifier_Letter
17350  * <li>Lo - Other_Letter
17351  * <li>Mn - Nonspacing_Mark
17352  * <li>Me - Enclosing_Mark
17353  * <li>Mc - Spacing_Mark
17354  * <li>Nd - Decimal_Number
17355  * <li>Nl - Letter_Number
17356  * <li>No - Other_Number
17357  * <li>Zs - Space_Separator
17358  * <li>Zl - Line_Separator
17359  * <li>Zp - Paragraph_Separator
17360  * <li>Cc - Control
17361  * <li>Cf - Format
17362  * <li>Co - Private_Use
17363  * <li>Cs - Surrogate
17364  * <li>Pd - Dash_Punctuation
17365  * <li>Ps - Open_Punctuation
17366  * <li>Pe - Close_Punctuation
17367  * <li>Pc - Connector_Punctuation
17368  * <li>Po - Other_Punctuation
17369  * <li>Sm - Math_Symbol
17370  * <li>Sc - Currency_Symbol
17371  * <li>Sk - Modifier_Symbol
17372  * <li>So - Other_Symbol
17373  * <li>Pi - Initial_Punctuation
17374  * <li>Pf - Final_Punctuation
17375  * </ul>
17376  * 
17377  * @protected
17378  * @param {number} num code point of the character to examine
17379  * @param {string} rangeName the name of the range to check
17380  * @param {Object} obj object containing the character range data
17381  * @return {boolean} true if the first character is within the named
17382  * range
17383  */
17384 CType._inRange = function(num, rangeName, obj) {
17385 	var range;
17386 	if (num < 0 || !rangeName || !obj) {
17387 		return false;
17388 	}
17389 	
17390 	range = obj[rangeName];
17391 	if (!range) {
17392 		return false;
17393 	}
17394 	
17395 	var compare = function(singlerange, target) {
17396 		if (singlerange.length === 1) {
17397 			return singlerange[0] - target;
17398 		} else {
17399 			return target < singlerange[0] ? singlerange[0] - target :
17400 				(target > singlerange[1] ? singlerange[1] - target : 0);
17401 		}
17402 	};
17403 	var result = SearchUtils.bsearch(num, range, compare);
17404 	return result < range.length && compare(range[result], num) === 0;
17405 };
17406 
17407 /**
17408  * Return whether or not the first character is within the named range
17409  * of Unicode characters. The valid list of range names are taken from 
17410  * the Unicode 6.0 spec. Characters in all ranges of Unicode are supported,
17411  * including those supported in Javascript via UTF-16. Currently, this method 
17412  * supports the following range names:
17413  * 
17414  * <ul>
17415  * <li><i>ascii</i> - basic ASCII
17416  * <li><i>latin</i> - Latin, Latin Extended Additional, Latin-1 supplement, Latin Extended-C, Latin Extended-D, Latin Extended-E
17417  * <li><i>armenian</i>
17418  * <li><i>greek</i> - Greek, Greek Extended
17419  * <li><i>cyrillic</i> - Cyrillic, Cyrillic Extended-A, Cyrillic Extended-B, Cyrillic Extended-C, Cyrillic Supplement
17420  * <li><i>georgian</i> - Georgian, Georgian Supplement
17421  * <li><i>glagolitic</i> - Glagolitic, Glagolitic Supplement
17422  * <li><i>gothic</i>
17423  * <li><i>ogham</i>
17424  * <li><i>oldpersian</i>
17425  * <li><i>runic</i>
17426  * <li><i>ipa</i> - IPA, Phonetic Extensions, Phonetic Extensions Supplement
17427  * <li><i>phonetic</i>
17428  * <li><i>modifiertone</i> - Modifier Tone Letters
17429  * <li><i>spacing</i>
17430  * <li><i>diacritics</i>
17431  * <li><i>halfmarks</i> - Combining Half Marks
17432  * <li><i>small</i> - Small Form Variants
17433  * <li><i>bamum</i> - Bamum, Bamum Supplement
17434  * <li><i>ethiopic</i> - Ethiopic, Ethiopic Extended, Ethiopic Extended-A
17435  * <li><i>nko</i>
17436  * <li><i>osmanya</i>
17437  * <li><i>tifinagh</i>
17438  * <li><i>val</i>
17439  * <li><i>arabic</i> - Arabic, Arabic Supplement, Arabic Presentation Forms-A, 
17440  * Arabic Presentation Forms-B, Arabic Mathematical Alphabetic Symbols
17441  * <li><i>carlan</i>
17442  * <li><i>hebrew</i>
17443  * <li><i>mandaic</i>
17444  * <li><i>samaritan</i>
17445  * <li><i>syriac</i>
17446  * <li><i>mongolian</i>
17447  * <li><i>phagspa</i>
17448  * <li><i>tibetan</i>
17449  * <li><i>bengali</i>
17450  * <li><i>devanagari</i> - Devanagari, Devanagari Extended
17451  * <li><i>gujarati</i>
17452  * <li><i>gurmukhi</i>
17453  * <li><i>kannada</i>
17454  * <li><i>lepcha</i>
17455  * <li><i>limbu</i>
17456  * <li><i>malayalam</i>
17457  * <li><i>meetaimayek</i>
17458  * <li><i>olchiki</i>
17459  * <li><i>oriya</i>
17460  * <li><i>saurashtra</i>
17461  * <li><i>sinhala</i>
17462  * <li><i>sylotinagri</i> - Syloti Nagri
17463  * <li><i>tangut</i>
17464  * <li><i>tamil</i>
17465  * <li><i>telugu</i>
17466  * <li><i>thaana</i>
17467  * <li><i>vedic</i>
17468  * <li><i>batak</i>
17469  * <li><i>balinese</i>
17470  * <li><i>buginese</i>
17471  * <li><i>cham</i>
17472  * <li><i>javanese</i>
17473  * <li><i>kayahli</i>
17474  * <li><i>khmer</i>
17475  * <li><i>lao</i>
17476  * <li><i>myanmar</i> - Myanmar, Myanmar Extended-A, Myanmar Extended-B
17477  * <li><i>newtailue</i>
17478  * <li><i>rejang</i>
17479  * <li><i>sundanese</i> - Sundanese, Sundanese Supplement
17480  * <li><i>taile</i>
17481  * <li><i>taitham</i>
17482  * <li><i>taiviet</i>
17483  * <li><i>thai</i>
17484  * <li><i>buhld</i>
17485  * <li><i>hanunoo</i>
17486  * <li><i>tagalog</i>
17487  * <li><i>tagbanwa</i>
17488  * <li><i>bopomofo</i> - Bopomofo, Bopomofo Extended
17489  * <li><i>cjk</i> - the CJK unified ideographs (Han), CJK Unified Ideographs
17490  *  Extension A, CJK Unified Ideographs Extension B, CJK Unified Ideographs 
17491  *  Extension C, CJK Unified Ideographs Extension D, Ideographic Description 
17492  *  Characters (=isIdeo())
17493  * <li><i>cjkcompatibility</i> - CJK Compatibility, CJK Compatibility 
17494  * Ideographs, CJK Compatibility Forms, CJK Compatibility Ideographs Supplement
17495  * <li><i>cjkradicals</i> - the CJK radicals, KangXi radicals
17496  * <li><i>hangul</i> - Hangul Jamo, Hangul Syllables, Hangul Jamo Extended-A, 
17497  * Hangul Jamo Extended-B, Hangul Compatibility Jamo
17498  * <li><i>cjkpunct</i> - CJK symbols and punctuation
17499  * <li><i>cjkstrokes</i> - CJK strokes
17500  * <li><i>hiragana</i>
17501  * <li><i>katakana</i> - Katakana, Katakana Phonetic Extensions, Kana Supplement
17502  * <li><i>kanbun</i>
17503  * <li><i>lisu</i>
17504  * <li><i>yi</i> - Yi Syllables, Yi Radicals
17505  * <li><i>cherokee</i>
17506  * <li><i>canadian</i> - Unified Canadian Aboriginal Syllabics, Unified Canadian 
17507  * Aboriginal Syllabics Extended
17508  * <li><i>presentation</i> - Alphabetic presentation forms
17509  * <li><i>vertical</i> - Vertical Forms
17510  * <li><i>width</i> - Halfwidth and Fullwidth Forms
17511  * <li><i>punctuation</i> - General punctuation, Supplemental Punctuation
17512  * <li><i>box</i> - Box Drawing
17513  * <li><i>block</i> - Block Elements
17514  * <li><i>letterlike</i> - Letterlike symbols
17515  * <li><i>mathematical</i> - Mathematical alphanumeric symbols, Miscellaneous 
17516  * Mathematical Symbols-A, Miscellaneous Mathematical Symbols-B
17517  * <li><i>enclosedalpha</i> - Enclosed alphanumerics, Enclosed Alphanumeric Supplement
17518  * <li><i>enclosedcjk</i> - Enclosed CJK letters and months, Enclosed Ideographic Supplement
17519  * <li><i>cjkcompatibility</i> - CJK compatibility
17520  * <li><i>apl</i> - APL symbols
17521  * <li><i>controlpictures</i> - Control pictures
17522  * <li><i>misc</i> - Miscellaneous technical
17523  * <li><i>ocr</i> - Optical character recognition (OCR)
17524  * <li><i>combining</i> - Combining Diacritical Marks, Combining Diacritical Marks 
17525  * for Symbols, Combining Diacritical Marks Supplement, Combining Diacritical Marks Extended
17526  * <li><i>digits</i> - ASCII digits (=isDigit())
17527  * <li><i>indicnumber</i> - Common Indic Number Forms
17528  * <li><i>numbers</i> - Number forms
17529  * <li><i>supersub</i> - Superscripts and Subscripts
17530  * <li><i>arrows</i> - Arrows, Miscellaneous Symbols and Arrows, Supplemental Arrows-A,
17531  * Supplemental Arrows-B, Supplemental Arrows-C
17532  * <li><i>operators</i> - Mathematical operators, supplemental 
17533  * mathematical operators 
17534  * <li><i>geometric</i> - Geometric shapes, Geometric shapes extended
17535  * <li><i>ancient</i> - Ancient symbols
17536  * <li><i>braille</i> - Braille patterns
17537  * <li><i>currency</i> - Currency symbols
17538  * <li><i>dingbats</i>
17539  * <li><i>gamesymbols</i>
17540  * <li><i>yijing</i> - Yijing Hexagram Symbols
17541  * <li><i>specials</i>
17542  * <li><i>variations</i> - Variation Selectors, Variation Selectors Supplement
17543  * <li><i>privateuse</i> - Private Use Area, Supplementary Private Use Area-A, 
17544  * Supplementary Private Use Area-B
17545  * <li><i>supplementarya</i> - Supplementary private use area-A
17546  * <li><i>supplementaryb</i> - Supplementary private use area-B
17547  * <li><i>highsurrogates</i> - High Surrogates, High Private Use Surrogates
17548  * <li><i>lowsurrogates</i>
17549  * <li><i>reserved</i>
17550  * <li><i>noncharacters</i>
17551  * <li><i>copticnumber</i> - coptic epact numbers
17552  * <li><i>oldpermic</i> - old permic
17553  * <li><i>albanian</i> - albanian
17554  * <li><i>lineara</i> - linear a
17555  * <li><i>meroitic</i> - meroitic cursive
17556  * <li><i>oldnortharabian</i> - old north arabian
17557  * <li><i>oldhungarian</i> - Supplementary private use area-A
17558  * <li><i>sorasompeng</i> - sora sompeng
17559  * <li><i>warangciti</i> - warang citi
17560  * <li><i>paucinhau</i> - pau cin hau
17561  * <li><i>bassavah</i> - bassa vah
17562  * <li><i>pahawhhmong</i> - pahawh hmong
17563  * <li><i>shorthandformat</i> - shorthand format controls
17564  * <li><i>suttonsignwriting</i> - sutton signwriting
17565  * <li><i>pictographs</i> - miscellaneous symbols and pictographs, supplemental symbols and pictographs
17566  * <li><i>ornamentaldingbats</i> - ornamental dingbats
17567  * </ul><p>
17568  * 
17569  * 
17570  * @protected
17571  * @param {string|IString|number} ch character or code point to examine
17572  * @param {string} rangeName the name of the range to check
17573  * @return {boolean} true if the first character is within the named
17574  * range
17575  */
17576 CType.withinRange = function(ch, rangeName) {
17577 	if (!rangeName) {
17578 		return false;
17579 	}
17580 	var num;
17581 	switch (typeof(ch)) {
17582 		case 'number':
17583 			num = ch;
17584 			break;
17585 		case 'string':
17586 			num = IString.toCodePoint(ch, 0);
17587 			break;
17588 		case 'undefined':
17589 			return false;
17590 		default:
17591 			num = ch._toCodePoint(0);
17592 			break;
17593 	}
17594 
17595 	return CType._inRange(num, rangeName.toLowerCase(), ilib.data.ctype);
17596 };
17597 
17598 /**
17599  * @protected
17600  * @param {boolean} sync
17601  * @param {Object|undefined} loadParams
17602  * @param {function(*)|undefined} onLoad
17603  */
17604 CType._init = function(sync, loadParams, onLoad) {
17605 	CType._load("ctype", sync, loadParams, onLoad);
17606 };
17607 
17608 /**
17609  * @protected
17610  * @param {string} name
17611  * @param {boolean} sync
17612  * @param {Object|undefined} loadParams
17613  * @param {function(*)|undefined} onLoad
17614  */
17615 CType._load = function (name, sync, loadParams, onLoad) {
17616 	if (!ilib.data[name]) {
17617 		var loadName = name ? name + ".json" : "CType.json";
17618 		Utils.loadData({
17619 			object: "CType",
17620 			name: loadName,
17621 			locale: "-",
17622 			nonlocale: true,
17623 			sync: sync,
17624 			loadParams: loadParams, 
17625 			callback: ilib.bind(this, function(ct) {
17626 				ilib.data[name] = ct;
17627 				if (onLoad && typeof(onLoad) === 'function') {
17628 					onLoad(ilib.data[name]);
17629 				}
17630 			})
17631 		});
17632 	} else {
17633 		if (onLoad && typeof(onLoad) === 'function') {
17634 			onLoad(ilib.data[name]);
17635 		}
17636 	}
17637 };
17638 
17639 
17640 
17641 /*< isDigit.js */
17642 /*
17643  * isDigit.js - Character type is digit
17644  *
17645  * Copyright © 2012-2015, JEDLSoft
17646  *
17647  * Licensed under the Apache License, Version 2.0 (the "License");
17648  * you may not use this file except in compliance with the License.
17649  * You may obtain a copy of the License at
17650  *
17651  *     http://www.apache.org/licenses/LICENSE-2.0
17652  *
17653  * Unless required by applicable law or agreed to in writing, software
17654  * distributed under the License is distributed on an "AS IS" BASIS,
17655  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17656  *
17657  * See the License for the specific language governing permissions and
17658  * limitations under the License.
17659  */
17660 
17661 // !depends CType.js IString.js ilib.js
17662 
17663 // !data ctype ctype_n
17664 
17665 
17666 /**
17667  * Return whether or not the first character is a digit character in the
17668  * Latin script.<p>
17669  *
17670  * @static
17671  * @param {string|IString|number} ch character or code point to examine
17672  * @return {boolean} true if the first character is a digit character in the
17673  * Latin script.
17674  */
17675 var isDigit = function (ch) {
17676 	var num;
17677 	switch (typeof(ch)) {
17678 		case 'number':
17679 			num = ch;
17680 			break;
17681 		case 'string':
17682 			num = IString.toCodePoint(ch, 0);
17683 			break;
17684 		case 'undefined':
17685 			return false;
17686 		default:
17687 			num = ch._toCodePoint(0);
17688 			break;
17689 	}
17690 	return ilib.data.ctype ? CType._inRange(num, 'Nd', ilib.data.ctype_n) : (num >= 0x30 && num <= 0x39);
17691 };
17692 
17693 /**
17694  * @protected
17695  * @param {boolean} sync
17696  * @param {Object|undefined} loadParams
17697  * @param {function(*)|undefined} onLoad
17698  */
17699 isDigit._init = function (sync, loadParams, onLoad) {
17700     CType._load("ctype_n", sync, loadParams, onLoad);
17701 };
17702 
17703 
17704 
17705 /*< isSpace.js */
17706 /*
17707  * isSpace.js - Character type is space char
17708  *
17709  * Copyright © 2012-2015, JEDLSoft
17710  *
17711  * Licensed under the Apache License, Version 2.0 (the "License");
17712  * you may not use this file except in compliance with the License.
17713  * You may obtain a copy of the License at
17714  *
17715  *     http://www.apache.org/licenses/LICENSE-2.0
17716  *
17717  * Unless required by applicable law or agreed to in writing, software
17718  * distributed under the License is distributed on an "AS IS" BASIS,
17719  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17720  *
17721  * See the License for the specific language governing permissions and
17722  * limitations under the License.
17723  */
17724 
17725 // !depends CType.js IString.js
17726 
17727 // !data ctype ctype_z
17728 
17729 
17730 
17731 /**
17732  * Return whether or not the first character is a whitespace character.<p>
17733  *
17734  * @static
17735  * @param {string|IString|number} ch character or code point to examine
17736  * @return {boolean} true if the first character is a whitespace character.
17737  */
17738 var isSpace = function (ch) {
17739 	var num;
17740 	switch (typeof(ch)) {
17741 		case 'number':
17742 			num = ch;
17743 			break;
17744 		case 'string':
17745 			num = IString.toCodePoint(ch, 0);
17746 			break;
17747 		case 'undefined':
17748 			return false;
17749 		default:
17750 			num = ch._toCodePoint(0);
17751 			break;
17752 	}
17753 
17754 	return ilib.data.ctype && ilib.data.ctype_z ?
17755 	    (CType._inRange(num, 'space', ilib.data.ctype) ||
17756 		CType._inRange(num, 'Zs', ilib.data.ctype_z) ||
17757 		CType._inRange(num, 'Zl', ilib.data.ctype_z) ||
17758 		CType._inRange(num, 'Zp', ilib.data.ctype_z)) :
17759 		(ch === ' ' || num === 0xA0 ||
17760 		(num >= 0x09 && num <= 0x0D));
17761 };
17762 
17763 /**
17764  * @protected
17765  * @param {boolean} sync
17766  * @param {Object|undefined} loadParams
17767  * @param {function(*)|undefined} onLoad
17768  */
17769 isSpace._init = function (sync, loadParams, onLoad) {
17770 	CType._load("ctype_z", sync, loadParams, function () {
17771 		CType._init(sync, loadParams, onLoad);
17772 	});
17773 };
17774 
17775 
17776 /*< Currency.js */
17777 /*
17778  * Currency.js - Currency definition
17779  * 
17780  * Copyright © 2012-2015, JEDLSoft
17781  *
17782  * Licensed under the Apache License, Version 2.0 (the "License");
17783  * you may not use this file except in compliance with the License.
17784  * You may obtain a copy of the License at
17785  *
17786  *     http://www.apache.org/licenses/LICENSE-2.0
17787  *
17788  * Unless required by applicable law or agreed to in writing, software
17789  * distributed under the License is distributed on an "AS IS" BASIS,
17790  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17791  *
17792  * See the License for the specific language governing permissions and
17793  * limitations under the License.
17794  */
17795 
17796 // !depends ilib.js Utils.js Locale.js LocaleInfo.js
17797 
17798 // !data currency
17799 
17800 
17801 /**
17802  * @class
17803  * Create a new currency information instance. Instances of this class encode 
17804  * information about a particular currency.<p>
17805  * 
17806  * Note: that if you are looking to format currency for display, please see
17807  * the number formatting class {NumFmt}. This class only gives information
17808  * about currencies.<p> 
17809  * 
17810  * The options can contain any of the following properties:
17811  * 
17812  * <ul>
17813  * <li><i>locale</i> - specify the locale for this instance
17814  * <li><i>code</i> - find info on a specific currency with the given ISO 4217 code 
17815  * <li><i>sign</i> - search for a currency that uses this sign
17816  * <li><i>onLoad</i> - a callback function to call when the currency data is fully 
17817  * loaded. When the onLoad option is given, this class will attempt to
17818  * load any missing locale data using the ilib loader callback.
17819  * When the constructor is done (even if the data is already preassembled), the 
17820  * onLoad function is called with the current instance as a parameter, so this
17821  * callback can be used with preassembled or dynamic loading or a mix of the two. 
17822  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
17823  * asynchronously. If this option is given as "false", then the "onLoad"
17824  * callback must be given, as the instance returned from this constructor will
17825  * not be usable for a while.
17826  * <li><i>loadParams</i> - an object containing parameters to pass to the 
17827  * loader callback function when locale data is missing. The parameters are not
17828  * interpretted or modified in any way. They are simply passed along. The object 
17829  * may contain any property/value pairs as long as the calling code is in
17830  * agreement with the loader callback function as to what those parameters mean.
17831  * </ul>
17832  * 
17833  * When searching for a currency by its sign, this class cannot guarantee 
17834  * that it will return info about a specific currency. The reason is that currency 
17835  * signs are sometimes shared between different currencies and the sign is 
17836  * therefore ambiguous. If you need a 
17837  * guarantee, find the currency using the code instead.<p>
17838  * 
17839  * The way this class finds a currency by sign is the following. If the sign is 
17840  * unambiguous, then
17841  * the currency is returned. If there are multiple currencies that use the same
17842  * sign, and the current locale uses that sign, then the default currency for
17843  * the current locale is returned. If there are multiple, but the current locale
17844  * does not use that sign, then the currency with the largest circulation is
17845  * returned. For example, if you are in the en-GB locale, and the sign is "$",
17846  * then this class will notice that there are multiple currencies with that
17847  * sign (USD, CAD, AUD, HKD, MXP, etc.) Since "$" is not used in en-GB, it will 
17848  * pick the one with the largest circulation, which in this case is the US Dollar
17849  * (USD).<p>
17850  * 
17851  * If neither the code or sign property is set, the currency that is most common 
17852  * for the locale
17853  * will be used instead. If the locale is not set, the default locale will be used.
17854  * If the code is given, but it is not found in the list of known currencies, this
17855  * constructor will throw an exception. If the sign is given, but it is not found,
17856  * this constructor will default to the currency for the current locale. If both
17857  * the code and sign properties are given, then the sign property will be ignored
17858  * and only the code property used. If the locale is given, but it is not a known
17859  * locale, this class will default to the default locale instead.<p>
17860  * 
17861  * 
17862  * @constructor
17863  * @param options {Object} a set of properties to govern how this instance is constructed.
17864  * @throws "currency xxx is unknown" when the given currency code is not in the list of 
17865  * known currencies. xxx is replaced with the requested code.
17866  */
17867 var Currency = function (options) {
17868 	if (options) {
17869 		if (options.code) {
17870 			this.code = options.code;
17871 		}
17872 		if (options.locale) {
17873 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
17874 		}
17875 		if (options.sign) {
17876 			this.sign = options.sign;
17877 		}
17878 		if (options.loadParams) {
17879 			this.loadParams = options.loadParams;
17880 		}
17881 	} else {
17882 	    options = {sync: true};
17883 	}
17884 	
17885     if (typeof(options.sync) === 'undefined') {
17886         options.sync = true;
17887     }
17888 	
17889 	this.locale = this.locale || new Locale();
17890 	if (typeof(ilib.data.currency) === 'undefined') {
17891 		Utils.loadData({
17892 			name: "currency.json",
17893 			object: "Currency", 
17894 			locale: "-",
17895 			sync: this.sync, 
17896 			loadParams: this.loadParams, 
17897 			callback: ilib.bind(this, function(currency) {
17898 				ilib.data.currency = currency;
17899 				this._loadLocinfo(options);
17900 			})
17901 		});
17902 	} else {
17903 		this._loadLocinfo(options);
17904 	}
17905 };
17906 
17907 /**
17908  * Return an array of the ids for all ISO 4217 currencies that
17909  * this copy of ilib knows about.
17910  * 
17911  * @static
17912  * @return {Array.<string>} an array of currency ids that this copy of ilib knows about.
17913  */
17914 Currency.getAvailableCurrencies = function() {
17915 	var ret = [],
17916 		cur,
17917 		currencies = new ResBundle({
17918 			name: "currency"
17919 		}).getResObj();
17920 	
17921 	for (cur in currencies) {
17922 		if (cur && currencies[cur]) {
17923 			ret.push(cur);
17924 		}
17925 	}
17926 	
17927 	return ret;
17928 };
17929 
17930 Currency.prototype = {
17931 	/**
17932 	 * @private
17933 	 */
17934 	_loadLocinfo: function(options) {
17935 		new LocaleInfo(this.locale, {
17936 		    sync: options.sync,
17937 		    loadParams: options.loadParams,
17938 			onLoad: ilib.bind(this, function (li) {
17939 			    var currInfo;
17940 
17941 			    this.locinfo = li;
17942 			    if (this.code) {
17943 			        currInfo = ilib.data.currency[this.code];
17944 			        if (!currInfo) {
17945 			            if (options.sync) {
17946 			                throw "currency " + this.code + " is unknown";
17947 			            } else if (typeof(options.onLoad) === "function") {
17948 			                options.onLoad(undefined);
17949 			                return;
17950 			            }
17951 			        }
17952 			    } else if (this.sign) {
17953 			        currInfo = ilib.data.currency[this.sign]; // maybe it is really a code...
17954 			        if (typeof(currInfo) !== 'undefined') {
17955 			            this.code = this.sign;
17956 			        } else {
17957 			            this.code = this.locinfo.getCurrency();
17958 			            currInfo = ilib.data.currency[this.code];
17959 			            if (currInfo.sign !== this.sign) {
17960 			                // current locale does not use the sign, so search for it
17961 			                for (var cur in ilib.data.currency) {
17962 			                    if (cur && ilib.data.currency[cur]) {
17963 			                        currInfo = ilib.data.currency[cur];
17964 			                        if (currInfo.sign === this.sign) {
17965 			                            // currency data is already ordered so that the currency with the
17966 			                            // largest circulation is at the beginning, so all we have to do
17967 			                            // is take the first one in the list that matches
17968 			                            this.code = cur;
17969 			                            break;
17970 			                        }
17971 			                    }
17972 			                }
17973 			            }
17974 			        }
17975 			    }
17976 
17977 			    if (!currInfo || !this.code) {
17978 			        this.code = this.locinfo.getCurrency();
17979 			        currInfo = ilib.data.currency[this.code];
17980 			    }
17981 
17982 			    this.name = currInfo.name;
17983 			    this.fractionDigits = currInfo.decimals;
17984 			    this.sign = currInfo.sign;
17985 
17986 				if (typeof(options.onLoad) === 'function') {
17987 				    options.onLoad(this);
17988 				}
17989 			})
17990 		});
17991 	},
17992 	
17993 	/**
17994 	 * Return the ISO 4217 currency code for this instance.
17995 	 * @return {string} the ISO 4217 currency code for this instance
17996 	 */
17997 	getCode: function () {
17998 		return this.code;
17999 	},
18000 	
18001 	/**
18002 	 * Return the default number of fraction digits that is typically used
18003 	 * with this type of currency.
18004 	 * @return {number} the number of fraction digits for this currency
18005 	 */
18006 	getFractionDigits: function () {
18007 		return this.fractionDigits;
18008 	},
18009 	
18010 	/**
18011 	 * Return the sign commonly used to represent this currency.
18012 	 * @return {string} the sign commonly used to represent this currency
18013 	 */
18014 	getSign: function () {
18015 		return this.sign;
18016 	},
18017 	
18018 	/**
18019 	 * Return the name of the currency in English.
18020 	 * @return {string} the name of the currency in English
18021 	 */
18022 	getName: function () {
18023 		return this.name;
18024 	},
18025 	
18026 	/**
18027 	 * Return the locale for this currency. If the options to the constructor 
18028 	 * included a locale property in order to find the currency that is appropriate
18029 	 * for that locale, then the locale is returned here. If the options did not
18030 	 * include a locale, then this method returns undefined.
18031 	 * @return {Locale} the locale used in the constructor of this instance,
18032 	 * or undefined if no locale was given in the constructor
18033 	 */
18034 	getLocale: function () {
18035 		return this.locale;
18036 	}
18037 };
18038 
18039 
18040 
18041 /*< INumber.js */
18042 /*
18043  * INumber.js - Parse a number in any locale
18044  * 
18045  * Copyright © 2012-2015, JEDLSoft
18046  *
18047  * Licensed under the Apache License, Version 2.0 (the "License");
18048  * you may not use this file except in compliance with the License.
18049  * You may obtain a copy of the License at
18050  *
18051  *     http://www.apache.org/licenses/LICENSE-2.0
18052  *
18053  * Unless required by applicable law or agreed to in writing, software
18054  * distributed under the License is distributed on an "AS IS" BASIS,
18055  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18056  *
18057  * See the License for the specific language governing permissions and
18058  * limitations under the License.
18059  */
18060 
18061 /*
18062 !depends 
18063 ilib.js 
18064 Locale.js 
18065 isDigit.js 
18066 isSpace.js
18067 LocaleInfo.js
18068 Currency.js
18069 */
18070 
18071 
18072 
18073 
18074 
18075 
18076 /**
18077  * @class
18078  * Parse a string as a number, ignoring all locale-specific formatting.<p>
18079  * 
18080  * This class is different from the standard Javascript parseInt() and parseFloat() 
18081  * functions in that the number to be parsed can have formatting characters in it 
18082  * that are not supported by those two
18083  * functions, and it handles numbers written in other locales properly. For example, 
18084  * if you pass the string "203,231.23" to the parseFloat() function in Javascript, it 
18085  * will return you the number 203. The INumber class will parse it correctly and 
18086  * the value() function will return the number 203231.23. If you pass parseFloat() the 
18087  * string "203.231,23" with the locale set to de-DE, it will return you 203 again. This
18088  * class will return the correct number 203231.23 again.<p>
18089  * 
18090  * The options object may contain any of the following properties:
18091  * 
18092  * <ul>
18093  * <li><i>locale</i> - specify the locale of the string to parse. This is used to
18094  * figure out what the decimal point character is. If not specified, the default locale
18095  * for the app or browser is used.
18096  * <li><i>type</i> - specify whether this string should be interpretted as a number,
18097  * currency, or percentage amount. When the number is interpretted as a currency
18098  * amount, the getCurrency() method will return something useful, otherwise it will
18099  * return undefined. If
18100  * the number is to be interpretted as percentage amount and there is a percentage sign
18101  * in the string, then the number will be returned
18102  * as a fraction from the valueOf() method. If there is no percentage sign, then the 
18103  * number will be returned as a regular number. That is "58.3%" will be returned as the 
18104  * number 0.583 but "58.3" will be returned as 58.3. Valid values for this property 
18105  * are "number", "currency", and "percentage". Default if this is not specified is
18106  * "number".
18107  * <li><i>onLoad</i> - a callback function to call when the locale data is fully 
18108  * loaded. When the onLoad option is given, this class will attempt to
18109  * load any missing locale data using the ilib loader callback.
18110  * When the constructor is done (even if the data is already preassembled), the 
18111  * onLoad function is called with the current instance as a parameter, so this
18112  * callback can be used with preassembled or dynamic loading or a mix of the two. 
18113  * 
18114  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
18115  * asynchronously. If this option is given as "false", then the "onLoad"
18116  * callback must be given, as the instance returned from this constructor will
18117  * not be usable for a while.
18118  *  
18119  * <li><i>loadParams</i> - an object containing parameters to pass to the 
18120  * loader callback function when locale data is missing. The parameters are not
18121  * interpretted or modified in any way. They are simply passed along. The object 
18122  * may contain any property/value pairs as long as the calling code is in
18123  * agreement with the loader callback function as to what those parameters mean.
18124  * </ul>
18125  * <p>
18126  * 
18127  * This class is named INumber ("ilib number") so as not to conflict with the 
18128  * built-in Javascript Number class.
18129  * 
18130  * @constructor
18131  * @param {string|number|INumber|Number|undefined} str a string to parse as a number, or a number value
18132  * @param {Object=} options Options controlling how the instance should be created 
18133  */
18134 var INumber = function (str, options) {
18135 	var i, stripped = "", 
18136 		sync = true;
18137 	
18138 	this.locale = new Locale();
18139 	this.type = "number";
18140 	
18141 	if (options) {
18142 		if (options.locale) {
18143 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
18144 		}
18145 		if (options.type) {
18146 			switch (options.type) {
18147 				case "number":
18148 				case "currency":
18149 				case "percentage":
18150 					this.type = options.type;
18151 					break;
18152 				default:
18153 					break;
18154 			}
18155 		}
18156 		if (typeof(options.sync) !== 'undefined') {
18157 			sync = !!options.sync;
18158 		}
18159 	} else {
18160 	    options = {sync: true};
18161 	}
18162 	
18163 	isDigit._init(sync, options.loadParams, ilib.bind(this, function() {
18164 		isSpace._init(sync, options.loadParams, ilib.bind(this, function() {
18165 			new LocaleInfo(this.locale, {
18166 				sync: sync,
18167 				loadParams: options.loadParams,
18168 				onLoad: ilib.bind(this, function (li) {
18169 				    this.li = li;
18170 					this.decimal = li.getDecimalSeparator();
18171 					var nativeDecimal = this.li.getNativeDecimalSeparator() || "";
18172 					
18173 					switch (typeof(str)) {
18174 					case 'string':
18175 						// stripping should work for all locales, because you just ignore all the
18176 						// formatting except the decimal char
18177 						var unary = true; // looking for the unary minus still?
18178 						var lastNumericChar = 0;
18179 						this.str = str || "0";
18180 						i = 0;
18181 						for (i = 0; i < this.str.length; i++) {
18182 							if (unary && this.str.charAt(i) === '-') {
18183 								unary = false;
18184 								stripped += this.str.charAt(i);
18185 								lastNumericChar = i;
18186 							} else if (isDigit(this.str.charAt(i))) {
18187 								stripped += this.str.charAt(i);
18188 								unary = false;
18189 								lastNumericChar = i;
18190 							} else if (this.str.charAt(i) === this.decimal || this.str.charAt(i) === nativeDecimal) {
18191 								stripped += "."; // always convert to period
18192 								unary = false;
18193 								lastNumericChar = i;
18194 							} // else ignore
18195 						}
18196 						// record what we actually parsed
18197 						this.parsed = this.str.substring(0, lastNumericChar+1);
18198 						
18199 						/** @type {number} */
18200 						this.value = parseFloat(this._mapToLatinDigits(stripped));
18201 						break;
18202 					case 'number':
18203 						this.str = "" + str;
18204 						this.value = str;
18205 						break;
18206 						
18207 					case 'object':
18208 						// call parseFloat to coerse the type to number
18209 						this.value = parseFloat(str.valueOf());
18210 						this.str = "" + this.value;
18211 						break;
18212 						
18213 					case 'undefined':
18214 						this.value = 0;
18215 						this.str = "0";
18216 						break;
18217 					}
18218 					
18219 					switch (this.type) {
18220 						default:
18221 							// don't need to do anything special for other types
18222 							break;
18223 						case "percentage":
18224 							if (this.str.indexOf(li.getPercentageSymbol()) !== -1) {
18225 								this.value /= 100;
18226 							}
18227 							break;
18228 						case "currency":
18229 							stripped = "";
18230 							i = 0;
18231 							while (i < this.str.length &&
18232 								   !isDigit(this.str.charAt(i)) &&
18233 								   !isSpace(this.str.charAt(i))) {
18234 								stripped += this.str.charAt(i++);
18235 							}
18236 							if (stripped.length === 0) {
18237 								while (i < this.str.length && 
18238 									   isDigit(this.str.charAt(i)) ||
18239 									   isSpace(this.str.charAt(i)) ||
18240 									   this.str.charAt(i) === '.' ||
18241 									   this.str.charAt(i) === ',' ) {
18242 									i++;
18243 								}
18244 								while (i < this.str.length && 
18245 									   !isDigit(this.str.charAt(i)) &&
18246 									   !isSpace(this.str.charAt(i))) {
18247 									stripped += this.str.charAt(i++);
18248 								}
18249 							}
18250 							new Currency({
18251 								locale: this.locale, 
18252 								sign: stripped,
18253 								sync: sync,
18254 								loadParams: options.loadParams,
18255 								onLoad: ilib.bind(this, function (cur) {
18256 									this.currency = cur;
18257 									if (options && typeof(options.onLoad) === 'function') {
18258 										options.onLoad(this);
18259 									}
18260 								})
18261 							});
18262 							return;
18263 					}
18264 					
18265 					if (options && typeof(options.onLoad) === 'function') {
18266 						options.onLoad(this);
18267 					}
18268 				})
18269 			});
18270 		}));
18271 	}));
18272 };
18273 
18274 INumber.prototype = {
18275     /**
18276      * @private
18277      */
18278     _mapToLatinDigits: function(str) {
18279         // only map if there are actual native digits
18280         var digits = this.li.getNativeDigits();
18281         if (!digits) return str;
18282         
18283         var digitMap = {};
18284         for (var i = 0; i < digits.length; i++) {
18285             digitMap[digits[i]] = String(i);
18286         }
18287         var decimal = this.li.getNativeDecimalSeparator();
18288         
18289         return str.split("").map(function(ch) {
18290             if (ch == decimal) return ".";
18291             return digitMap[ch] || ch;
18292         }).join("");
18293     },
18294     
18295 	/**
18296 	 * Return the locale for this formatter instance.
18297 	 * @return {Locale} the locale instance for this formatter
18298 	 */
18299 	getLocale: function () {
18300 		return this.locale;
18301 	},
18302 	
18303 	/**
18304 	 * Return the original string that this number instance was created with.
18305 	 * @return {string} the original string
18306 	 */
18307 	toString: function () {
18308 		return this.str;
18309 	},
18310 	
18311 	/**
18312 	 * If the type of this INumber instance is "currency", then the parser will attempt
18313 	 * to figure out which currency this amount represents. The amount can be written
18314 	 * with any of the currency signs or ISO 4217 codes that are currently
18315 	 * recognized by ilib, and the currency signs may occur before or after the
18316 	 * numeric portion of the string. If no currency can be recognized, then the 
18317 	 * default currency for the locale is returned. If multiple currencies can be
18318 	 * recognized (for example if the currency sign is "$"), then this method 
18319 	 * will prefer the one for the current locale. If multiple currencies can be
18320 	 * recognized, but none are used in the current locale, then the first currency
18321 	 * encountered will be used. This may produce random results, though the larger
18322 	 * currencies occur earlier in the list. For example, if the sign found in the
18323 	 * string is "$" and that is not the sign of the currency of the current locale
18324 	 * then the US dollar will be recognized, as it is the largest currency that uses
18325 	 * the "$" as its sign.
18326 	 * 
18327 	 * @return {Currency|undefined} the currency instance for this amount, or 
18328 	 * undefined if this INumber object is not of type currency
18329 	 */
18330 	getCurrency: function () {
18331 		return this.currency;
18332 	},
18333 	
18334 	/**
18335 	 * Return the value of this INumber object as a primitive number instance.
18336 	 * @return {number} the value of this number instance
18337 	 */
18338 	valueOf: function () {
18339 		return this.value;
18340 	}
18341 };
18342 
18343 
18344 /*< NumFmt.js */
18345 /*
18346  * NumFmt.js - Number formatter definition
18347  *
18348  * Copyright © 2012-2015, 2018 JEDLSoft
18349  *
18350  * Licensed under the Apache License, Version 2.0 (the "License");
18351  * you may not use this file except in compliance with the License.
18352  * You may obtain a copy of the License at
18353  *
18354  *     http://www.apache.org/licenses/LICENSE-2.0
18355  *
18356  * Unless required by applicable law or agreed to in writing, software
18357  * distributed under the License is distributed on an "AS IS" BASIS,
18358  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18359  *
18360  * See the License for the specific language governing permissions and
18361  * limitations under the License.
18362  */
18363 
18364 /*
18365 !depends
18366 ilib.js
18367 Locale.js
18368 LocaleInfo.js
18369 MathUtils.js
18370 Currency.js
18371 IString.js
18372 JSUtils.js
18373 */
18374 
18375 // !data localeinfo currency
18376 
18377 
18378 
18379 /**
18380  * @class
18381  * Create a new number formatter instance. Locales differ in the way that digits
18382  * in a formatted number are grouped, in the way the decimal character is represented,
18383  * etc. Use this formatter to get it right for any locale.<p>
18384  *
18385  * This formatter can format plain numbers, currency amounts, and percentage amounts.<p>
18386  *
18387  * As with all formatters, the recommended
18388  * practice is to create one formatter and use it multiple times to format various
18389  * numbers.<p>
18390  *
18391  * The options can contain any of the following properties:
18392  *
18393  * <ul>
18394  * <li><i>locale</i> - use the conventions of the specified locale when figuring out how to
18395  * format a number.
18396  * <li><i>type</i> - the type of this formatter. Valid values are "number", "currency", or
18397  * "percentage". If this property is not specified, the default is "number".
18398  * <li><i>currency</i> - the ISO 4217 3-letter currency code to use when the formatter type
18399  * is "currency". This property is required for currency formatting. If the type property
18400  * is "currency" and the currency property is not specified, the constructor will throw a
18401  * an exception.
18402  * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the
18403  * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print
18404  * the integral part of the number.
18405  * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should
18406  * appear in the formatted output. If the number does not have enough fractional digits
18407  * to reach this minimum, the number will be zero-padded at the end to get to the limit.
18408  * If the type of the formatter is "currency" and this
18409  * property is not specified, then the minimum fraction digits is set to the normal number
18410  * of digits used with that currency, which is almost always 0, 2, or 3 digits.
18411  * <li><i>significantDigits</i> - specify that max number of significant digits in the
18412  * formatted output. This applies before and after the decimal point. The amount is
18413  * rounded according to the rounding mode specified, or the rounding mode as given in
18414  * the locale information. If the significant digits and the max or min fraction digits
18415  * are both specified, this formatter will attempt to honour them both by choosing the
18416  * one that is smaller if there is a conflict. For example, if the max fraction digits
18417  * is 6 and the significant digits is 5 and the number to be formatted has a long
18418  * fraction, it will only format 5 digits. The default is "unlimited digits", which means
18419  * to format as many digits as the javascript engine can represent internally (usually
18420  * around 13-15 or so on a 64-bit machine).
18421  * <li><i>useNative</i> - the flag used to determaine whether to use the native script settings
18422  * for formatting the numbers .
18423  * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified,
18424  * this property governs how the least significant digits are rounded to conform to that
18425  * maximum. The value of this property is a string with one of the following values:
18426  * <ul>
18427  *   <li><i>up</i> - round away from zero
18428  *   <li><i>down</i> - round towards zero. This has the effect of truncating the number
18429  *   <li><i>ceiling</i> - round towards positive infinity
18430  *   <li><i>floor</i> - round towards negative infinity
18431  *   <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up.
18432  *   <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down.
18433  *   <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour
18434  *   <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour
18435  * </ul>
18436  * When the type of the formatter is "currency" and the <i>roundingMode</i> property is not
18437  * set, then the standard legal rounding rules for the locale are followed. If the type
18438  * is "number" or "percentage" and the <i>roundingMode</i> property is not set, then the
18439  * default mode is "halfdown".</i>.
18440  *
18441  * <li><i>style</i> - When the type of this formatter is "currency", the currency amount
18442  * can be formatted in the following styles: "common" and "iso". The common style is the
18443  * one commonly used in every day writing where the currency unit is represented using a
18444  * symbol. eg. "$57.35" for fifty-seven dollars and thirty five cents. The iso style is
18445  * the international style where the currency unit is represented using the ISO 4217 code.
18446  * eg. "USD 57.35" for the same amount. The default is "common" style if the style is
18447  * not specified.<p>
18448  *
18449  * When the type of this formatter is "number", the style can be one of the following:
18450  * <ul>
18451  *   <li><i>standard - format a fully specified floating point number properly for the locale
18452  *   <li><i>scientific</i> - use scientific notation for all numbers. That is, 1 integral
18453  *   digit, followed by a number of fractional digits, followed by an "e" which denotes
18454  *   exponentiation, followed digits which give the power of 10 in the exponent.
18455  *   <li><i>native</i> - format a floating point number using the native digits and
18456  *   formatting symbols for the script of the locale.
18457  *   <li><i>nogrouping</i> - format a floating point number without grouping digits for
18458  *   the integral portion of the number
18459  * </ul>
18460  * Note that if you specify a maximum number
18461  * of integral digits, the formatter with a standard style will give you standard
18462  * formatting for smaller numbers and scientific notation for larger numbers. The default
18463  * is standard style if this is not specified.
18464  *
18465  * <li><i>onLoad</i> - a callback function to call when the format data is fully
18466  * loaded. When the onLoad option is given, this class will attempt to
18467  * load any missing locale data using the ilib loader callback.
18468  * When the constructor is done (even if the data is already preassembled), the
18469  * onLoad function is called with the current instance as a parameter, so this
18470  * callback can be used with preassembled or dynamic loading or a mix of the two.
18471  *
18472  * <li>sync - tell whether to load any missing locale data synchronously or
18473  * asynchronously. If this option is given as "false", then the "onLoad"
18474  * callback must be given, as the instance returned from this constructor will
18475  * not be usable for a while.
18476  *
18477  * <li><i>loadParams</i> - an object containing parameters to pass to the
18478  * loader callback function when locale data is missing. The parameters are not
18479  * interpretted or modified in any way. They are simply passed along. The object
18480  * may contain any property/value pairs as long as the calling code is in
18481  * agreement with the loader callback function as to what those parameters mean.
18482  * </ul>
18483  * <p>
18484  *
18485  *
18486  * @constructor
18487  * @param {Object.<string,*>} options A set of options that govern how the formatter will behave
18488  */
18489 var NumFmt = function (options) {
18490 	var sync = true;
18491 	this.locale = new Locale();
18492 	/**
18493 	 * @private
18494 	 * @type {string}
18495 	 */
18496 	this.type = "number";
18497 	var loadParams = undefined;
18498 
18499 	if (options) {
18500 		if (options.locale) {
18501 			this.locale = (typeof (options.locale) === 'string') ? new Locale(options.locale) : options.locale;
18502 		}
18503 
18504 		if (options.type) {
18505 			if (options.type === 'number' ||
18506 				options.type === 'currency' ||
18507 				options.type === 'percentage') {
18508 				this.type = options.type;
18509 			}
18510 		}
18511 
18512 		if (options.currency) {
18513 			/**
18514 			 * @private
18515 			 * @type {string}
18516 			 */
18517 			this.currency = options.currency;
18518 		}
18519 
18520         if (typeof (options.maxFractionDigits) !== 'undefined') {
18521             /**
18522              * @private
18523              * @type {number|undefined}
18524              */
18525             this.maxFractionDigits = Number(options.maxFractionDigits);
18526         }
18527         if (typeof (options.minFractionDigits) !== 'undefined') {
18528             /**
18529              * @private
18530              * @type {number|undefined}
18531              */
18532             this.minFractionDigits = Number(options.minFractionDigits);
18533             // enforce the limits to avoid JS exceptions
18534             if (this.minFractionDigits < 0) {
18535                 this.minFractionDigits = 0;
18536             }
18537             if (this.minFractionDigits > 20) {
18538                 this.minFractionDigits = 20;
18539             }
18540         }
18541         if (typeof (options.significantDigits) !== 'undefined') {
18542             /**
18543              * @private
18544              * @type {number|undefined}
18545              */
18546             this.significantDigits = Number(options.significantDigits);
18547             // enforce the limits to avoid JS exceptions
18548             if (this.significantDigits < 1) {
18549                 this.significantDigits = 1;
18550             }
18551             if (this.significantDigits > 20) {
18552                 this.significantDigits = 20;
18553             }
18554         }
18555 		if (options.style) {
18556 			/**
18557 			 * @private
18558 			 * @type {string}
18559 			 */
18560 			this.style = options.style;
18561 		}
18562 		if (typeof(options.useNative) === 'boolean') {
18563 			/**
18564 			 * @private
18565 			 * @type {boolean}
18566 			 * */
18567 			this.useNative = options.useNative;
18568 		}
18569 		/**
18570 		 * @private
18571 		 * @type {string}
18572 		 */
18573 		this.roundingMode = options.roundingMode;
18574 
18575 		if (typeof(options.sync) === 'boolean') {
18576 			sync = options.sync;
18577 		}
18578 
18579 		loadParams = options.loadParams;
18580 	}
18581 
18582 	/**
18583 	 * @private
18584 	 * @type {LocaleInfo|undefined}
18585 	 */
18586 	this.localeInfo = undefined;
18587 
18588 	new LocaleInfo(this.locale, {
18589 		sync: sync,
18590 		loadParams: loadParams,
18591 		onLoad: ilib.bind(this, function (li) {
18592 			/**
18593 			 * @private
18594 			 * @type {LocaleInfo|undefined}
18595 			 */
18596 			this.localeInfo = li;
18597 
18598 			if (this.type === "number") {
18599 				this.templateNegative = new IString(this.localeInfo.getNegativeNumberFormat() || "-{n}");
18600 			} else if (this.type === "currency") {
18601 				var templates;
18602 
18603 				if (!this.currency || typeof (this.currency) != 'string') {
18604 					throw "A currency property is required in the options to the number formatter constructor when the type property is set to currency.";
18605 				}
18606 
18607 				new Currency({
18608 					locale: this.locale,
18609 					code: this.currency,
18610 					sync: sync,
18611 					loadParams: loadParams,
18612 					onLoad: ilib.bind(this, function (cur) {
18613 						this.currencyInfo = cur;
18614 						if (this.style !== "common" && this.style !== "iso") {
18615 							this.style = "common";
18616 						}
18617 
18618 						if (typeof(this.maxFractionDigits) !== 'number' && typeof(this.minFractionDigits) !== 'number') {
18619 							this.minFractionDigits = this.maxFractionDigits = this.currencyInfo.getFractionDigits();
18620 						}
18621 
18622 						templates = this.localeInfo.getCurrencyFormats();
18623 						this.template = new IString(templates[this.style] || templates.common);
18624 						this.templateNegative = new IString(templates[this.style + "Negative"] || templates["commonNegative"]);
18625 						this.sign = (this.style === "iso") ? this.currencyInfo.getCode() : this.currencyInfo.getSign();
18626 
18627 						if (!this.roundingMode) {
18628 							this.roundingMode = this.currencyInfo && this.currencyInfo.roundingMode;
18629 						}
18630 
18631 						this._init();
18632 
18633 						if (options && typeof (options.onLoad) === 'function') {
18634 							options.onLoad(this);
18635 						}
18636 					})
18637 				});
18638 				return;
18639 			} else if (this.type === "percentage") {
18640 				this.template =  new IString(this.localeInfo.getPercentageFormat() || "{n}%");
18641 				this.templateNegative = new IString(this.localeInfo.getNegativePercentageFormat() || this.localeInfo.getNegativeNumberFormat() + "%");
18642 			}
18643 
18644 			this._init();
18645 
18646 			if (options && typeof (options.onLoad) === 'function') {
18647 				options.onLoad(this);
18648 			}
18649 		})
18650 	});
18651 };
18652 
18653 /**
18654  * Return an array of available locales that this formatter can format
18655  * @static
18656  * @return {Array.<Locale>|undefined} an array of available locales
18657  */
18658 NumFmt.getAvailableLocales = function () {
18659 	return undefined;
18660 };
18661 
18662 /**
18663  * @private
18664  * @const
18665  * @type string
18666  */
18667 NumFmt.zeros = "0000000000000000000000000000000000000000000000000000000000000000000000";
18668 
18669 NumFmt.prototype = {
18670 	/**
18671 	 * Return true if this formatter uses native digits to format the number. If the useNative
18672 	 * option is given to the constructor, then this flag will be honoured. If the useNative
18673 	 * option is not given to the constructor, this this formatter will use native digits if
18674 	 * the locale typically uses native digits.
18675 	 *
18676 	 *  @return {boolean} true if this formatter will format with native digits, false otherwise
18677 	 */
18678 	getUseNative: function() {
18679 		if (typeof(this.useNative) === "boolean") {
18680 			return this.useNative;
18681 		}
18682 		return (this.localeInfo.getDigitsStyle() === "native");
18683 	},
18684 
18685 	/**
18686 	 * @private
18687 	 */
18688 	_init: function () {
18689 		if (this.maxFractionDigits < this.minFractionDigits) {
18690 			this.minFractionDigits = this.maxFractionDigits;
18691 		}
18692 
18693 		if (!this.roundingMode) {
18694 			this.roundingMode = this.localeInfo.getRoundingMode();
18695 		}
18696 
18697 		if (!this.roundingMode) {
18698 			this.roundingMode = "halfdown";
18699 		}
18700 
18701 		// set up the function, so we only have to figure it out once
18702 		// and not every time we do format()
18703 		this.round = MathUtils[this.roundingMode];
18704 		if (!this.round) {
18705 			this.roundingMode = "halfdown";
18706 			this.round = MathUtils[this.roundingMode];
18707 		}
18708 
18709 		if (this.style === "nogrouping") {
18710 			this.prigroupSize = this.secgroupSize = 0;
18711 		} else {
18712 			this.prigroupSize = this.localeInfo.getPrimaryGroupingDigits();
18713 			this.secgroupSize = this.localeInfo.getSecondaryGroupingDigits();
18714 			this.groupingSeparator = this.getUseNative() ? this.localeInfo.getNativeGroupingSeparator() : this.localeInfo.getGroupingSeparator();
18715 		}
18716 		this.decimalSeparator = this.getUseNative() ? this.localeInfo.getNativeDecimalSeparator() : this.localeInfo.getDecimalSeparator();
18717 
18718 		if (this.getUseNative()) {
18719 			var nd = this.localeInfo.getNativeDigits() || this.localeInfo.getDigits();
18720 			if (nd) {
18721 				this.digits = nd.split("");
18722 			}
18723 		}
18724 
18725 		this.exponentSymbol = this.localeInfo.getExponential() || "e";
18726 	},
18727 
18728     /**
18729      * Apply the constraints used in the current formatter to the given number. This will
18730      * will apply the maxFractionDigits, significantDigits, and rounding mode
18731      * constraints and return the result. The result is further
18732      * manipulated in the format method to produce the final formatted number string.
18733      * This method is intended for use by code that needs to use the same number that
18734      * this formatter instance uses for formatting before that number is turned into a
18735      * formatted string.
18736      *
18737      * @param {number} num the number to constrain
18738      * @returns {number} the number with the constraints applied to it
18739      */
18740     constrain: function(num) {
18741         var parts = ("" + num).split("."),
18742             result = num;
18743 
18744         // only apply the either significantDigits or the maxFractionDigits -- whichever results in a shorter fractional part
18745         if ((typeof(this.significantDigits) !== 'undefined' && this.significantDigits > 0) &&
18746             (typeof(this.maxFractionDigits) === 'undefined' || this.maxFractionDigits < 0 ||
18747                 parts[0].length + this.maxFractionDigits > this.significantDigits)) {
18748             result = MathUtils.significant(result, this.significantDigits, this.round);
18749         }
18750 
18751         if (typeof(this.maxFractionDigits) !== 'undefined' && this.maxFractionDigits > -1) {
18752             result = MathUtils.shiftDecimal(this.round(MathUtils.shiftDecimal(result, this.maxFractionDigits)), -this.maxFractionDigits);
18753         }
18754 
18755         return result;
18756     },
18757 
18758 	/**
18759 	 * Format the number using scientific notation as a positive number. Negative
18760 	 * formatting to be applied later.
18761 	 * @private
18762 	 * @param {number} num the number to format
18763 	 * @return {string} the formatted number
18764 	 */
18765 	_formatScientific: function (num) {
18766 		var n = new Number(num);
18767 		var formatted;
18768 
18769 		var str = n.toExponential(),
18770 			parts = str.split("e"),
18771 			significant = parts[0],
18772 			exponent = parts[1],
18773 			numparts,
18774 			integral,
18775 			fraction;
18776 
18777         if (this.maxFractionDigits > 0 || this.significantDigits > 0) {
18778             // if there is a max fraction digits setting, round the fraction to
18779             // the right length first by dividing or multiplying by powers of 10.
18780             // manipulate the fraction digits so as to
18781             // avoid the rounding errors of floating point numbers
18782             var maxDigits = (this.maxFractionDigits || 25) + 1;
18783             if (this.significantDigits > 0) {
18784                 maxDigits = Math.min(maxDigits, this.significantDigits);
18785             }
18786             significant = MathUtils.significant(Number(significant), maxDigits, this.round);
18787         }
18788 		numparts = ("" + significant).split(".");
18789 		integral = numparts[0];
18790 		fraction = numparts[1];
18791 
18792 		if (typeof(this.maxFractionDigits) !== 'undefined') {
18793 			fraction = fraction.substring(0, this.maxFractionDigits);
18794 		}
18795 		if (typeof(this.minFractionDigits) !== 'undefined') {
18796 			fraction = JSUtils.pad(fraction || "", this.minFractionDigits, true);
18797 		}
18798 		formatted = integral;
18799 		if (fraction.length) {
18800 			formatted += this.decimalSeparator + fraction;
18801 		}
18802 		formatted += this.exponentSymbol + exponent;
18803 		return formatted;
18804 	},
18805 
18806 	/**
18807 	 * Formats the number as a positive number. Negative formatting to be applied later.
18808 	 * @private
18809 	 * @param {number} num the number to format
18810 	 * @return {string} the formatted number
18811 	 */
18812 	_formatStandard: function (num) {
18813 		var i;
18814 		var k;
18815 
18816         var parts,
18817             integral,
18818             fraction,
18819             cycle,
18820             formatted;
18821 
18822         num = Math.abs(this.constrain(num));
18823 
18824 		parts = ("" + num).split(".");
18825 		integral = parts[0].toString();
18826 		fraction = parts[1];
18827 
18828 		if (this.minFractionDigits > 0) {
18829 			fraction = JSUtils.pad(fraction || "", this.minFractionDigits, true);
18830 		}
18831 
18832 		if (this.secgroupSize > 0) {
18833 			if (integral.length > this.prigroupSize) {
18834 				var size1 = this.prigroupSize;
18835 				var size2 = integral.length;
18836 				var size3 = size2 - size1;
18837 				integral = integral.slice(0, size3) + this.groupingSeparator + integral.slice(size3);
18838 				var num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator));
18839 				k = num_sec.length;
18840 				while (k > this.secgroupSize) {
18841 					var secsize1 = this.secgroupSize;
18842 					var secsize2 = num_sec.length;
18843 					var secsize3 = secsize2 - secsize1;
18844 					integral = integral.slice(0, secsize3) + this.groupingSeparator + integral.slice(secsize3);
18845 					num_sec = integral.substring(0, integral.indexOf(this.groupingSeparator));
18846 					k = num_sec.length;
18847 				}
18848 			}
18849 
18850 			formatted = integral;
18851 		} else if (this.prigroupSize !== 0) {
18852 			cycle = MathUtils.mod(integral.length - 1, this.prigroupSize);
18853 
18854 			formatted = "";
18855 
18856 			for (i = 0; i < integral.length - 1; i++) {
18857 				formatted += integral.charAt(i);
18858 				if (cycle === 0) {
18859 					formatted += this.groupingSeparator;
18860 				}
18861 				cycle = MathUtils.mod(cycle - 1, this.prigroupSize);
18862 			}
18863 			formatted += integral.charAt(integral.length - 1);
18864 		} else {
18865 			formatted = integral;
18866 		}
18867 
18868         if (fraction &&
18869             ((typeof(this.maxFractionDigits) === 'undefined' && typeof(this.significantDigits) === 'undefined') ||
18870               this.maxFractionDigits > 0 || this.significantDigits > 0)) {
18871             formatted += this.decimalSeparator;
18872             formatted += fraction;
18873         }
18874 
18875 		if (this.digits) {
18876 			formatted = JSUtils.mapString(formatted, this.digits);
18877 		}
18878 
18879 		return formatted;
18880 	},
18881 
18882 	/**
18883 	 * Format a number according to the settings of this number formatter instance.
18884 	 * @param num {number|string|INumber|Number} a floating point number to format
18885 	 * @return {string} a string containing the formatted number
18886 	 */
18887 	format: function (num) {
18888 		var formatted, n;
18889 
18890 		if (typeof (num) === 'undefined') {
18891 			return "";
18892 		}
18893 
18894 		// convert to a real primitive number type
18895 		n = Number(num);
18896 
18897 		if (this.type === "number") {
18898 			formatted = (this.style === "scientific") ?
18899 				this._formatScientific(n) :
18900 				this._formatStandard(n);
18901 
18902 			if (num < 0) {
18903 				formatted = this.templateNegative.format({n: formatted});
18904 			}
18905 		} else {
18906 			formatted = this._formatStandard(n);
18907 			var template = (n < 0) ? this.templateNegative : this.template;
18908 			formatted = template.format({
18909 				n: formatted,
18910 				s: this.sign
18911 			});
18912 		}
18913 
18914 		return formatted;
18915 	},
18916 
18917 	/**
18918 	 * Return the type of formatter. Valid values are "number", "currency", and
18919 	 * "percentage".
18920 	 *
18921 	 * @return {string} the type of formatter
18922 	 */
18923 	getType: function () {
18924 		return this.type;
18925 	},
18926 
18927 	/**
18928 	 * Return the locale for this formatter instance.
18929 	 * @return {Locale} the locale instance for this formatter
18930 	 */
18931 	getLocale: function () {
18932 		return this.locale;
18933 	},
18934 
18935 	/**
18936 	 * Returns true if this formatter groups together digits in the integral
18937 	 * portion of a number, based on the options set up in the constructor. In
18938 	 * most western European cultures, this means separating every 3 digits
18939 	 * of the integral portion of a number with a particular character.
18940 	 *
18941 	 * @return {boolean} true if this formatter groups digits in the integral
18942 	 * portion of the number
18943 	 */
18944 	isGroupingUsed: function () {
18945 		return (this.groupingSeparator !== 'undefined' && this.groupingSeparator.length > 0);
18946 	},
18947 
18948 	/**
18949 	 * Returns the maximum fraction digits set up in the constructor.
18950 	 *
18951 	 * @return {number} the maximum number of fractional digits this
18952 	 * formatter will format, or -1 for no maximum
18953 	 */
18954 	getMaxFractionDigits: function () {
18955 		return typeof (this.maxFractionDigits) !== 'undefined' ? this.maxFractionDigits : -1;
18956 	},
18957 
18958 	/**
18959 	 * Returns the minimum fraction digits set up in the constructor. If
18960 	 * the formatter has the type "currency", then the minimum fraction
18961 	 * digits is the amount of digits that is standard for the currency
18962 	 * in question unless overridden in the options to the constructor.
18963 	 *
18964 	 * @return {number} the minimum number of fractional digits this
18965 	 * formatter will format, or -1 for no minimum
18966 	 */
18967 	getMinFractionDigits: function () {
18968 		return typeof (this.minFractionDigits) !== 'undefined' ? this.minFractionDigits : -1;
18969 	},
18970 
18971    /**
18972      * Returns the significant digits set up in the constructor.
18973      *
18974      * @return {number} the number of significant digits this
18975      * formatter will format, or -1 for no minimum
18976      */
18977     getSignificantDigits: function () {
18978         return typeof (this.significantDigits) !== 'undefined' ? this.significantDigits : -1;
18979     },
18980 
18981 	/**
18982 	 * Returns the ISO 4217 code for the currency that this formatter formats.
18983 	 * IF the typeof this formatter is not "currency", then this method will
18984 	 * return undefined.
18985 	 *
18986 	 * @return {string} the ISO 4217 code for the currency that this formatter
18987 	 * formats, or undefined if this not a currency formatter
18988 	 */
18989 	getCurrency: function () {
18990 		return this.currencyInfo && this.currencyInfo.getCode();
18991 	},
18992 
18993 	/**
18994 	 * Returns the rounding mode set up in the constructor. The rounding mode
18995 	 * controls how numbers are rounded when the integral or fraction digits
18996 	 * of a number are limited.
18997 	 *
18998 	 * @return {string} the name of the rounding mode used in this formatter
18999 	 */
19000 	getRoundingMode: function () {
19001 		return this.roundingMode;
19002 	},
19003 
19004 	/**
19005 	 * If this formatter is a currency formatter, then the style determines how the
19006 	 * currency is denoted in the formatted output. This method returns the style
19007 	 * that this formatter will produce. (See the constructor comment for more about
19008 	 * the styles.)
19009 	 * @return {string} the name of the style this formatter will use to format
19010 	 * currency amounts, or "undefined" if this formatter is not a currency formatter
19011 	 */
19012 	getStyle: function () {
19013 		return this.style;
19014 	}
19015 };
19016 
19017 
19018 /*< ScriptInfo.js */
19019 /*
19020  * ScriptInfo.js - information about scripts
19021  * 
19022  * Copyright © 2012-2018, JEDLSoft
19023  *
19024  * Licensed under the Apache License, Version 2.0 (the "License");
19025  * you may not use this file except in compliance with the License.
19026  * You may obtain a copy of the License at
19027  *
19028  *     http://www.apache.org/licenses/LICENSE-2.0
19029  *
19030  * Unless required by applicable law or agreed to in writing, software
19031  * distributed under the License is distributed on an "AS IS" BASIS,
19032  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19033  *
19034  * See the License for the specific language governing permissions and
19035  * limitations under the License.
19036  */
19037 
19038 // !depends ilib.js Utils.js
19039 
19040 // !data scripts
19041 
19042 
19043 /**
19044  * @class
19045  * Create a new script info instance. This class encodes information about
19046  * scripts, which are sets of characters used in a writing system.<p>
19047  * 
19048  * The options object may contain any of the following properties:
19049  * 
19050  * <ul>
19051  * <li><i>onLoad</i> - a callback function to call when the script info object is fully 
19052  * loaded. When the onLoad option is given, the script info object will attempt to
19053  * load any missing locale data using the ilib loader callback.
19054  * When the constructor is done (even if the data is already preassembled), the 
19055  * onLoad function is called with the current instance as a parameter, so this
19056  * callback can be used with preassembled or dynamic loading or a mix of the two.
19057  * 
19058  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
19059  * asynchronously. If this option is given as "false", then the "onLoad"
19060  * callback must be given, as the instance returned from this constructor will
19061  * not be usable for a while. 
19062  *
19063  * <li><i>loadParams</i> - an object containing parameters to pass to the 
19064  * loader callback function when locale data is missing. The parameters are not
19065  * interpretted or modified in any way. They are simply passed along. The object 
19066  * may contain any property/value pairs as long as the calling code is in
19067  * agreement with the loader callback function as to what those parameters mean.
19068  * </ul>
19069  * 
19070  * 
19071  * @constructor
19072  * @param {string} script The ISO 15924 4-letter identifier for the script
19073  * @param {Object=} options parameters to initialize this instance 
19074  */
19075 var ScriptInfo = function(script, options) {
19076 	var sync = true,
19077 	    loadParams = undefined;
19078 	
19079 	this.script = script;
19080 	
19081 	if (options) {
19082 		if (typeof(options.sync) !== 'undefined') {
19083 			sync = !!options.sync;
19084 		}
19085 		
19086 		if (typeof(options.loadParams) !== 'undefined') {
19087 			loadParams = options.loadParams;
19088 		}
19089 	}
19090 
19091 	if (!ilib.data.scripts) {
19092 		Utils.loadData({
19093 			object: "ScriptInfo", 
19094 			locale: "-", 
19095 			name: "scripts.json", 
19096 			sync: sync, 
19097 			loadParams: loadParams, 
19098 			callback: ilib.bind(this, function (info) {
19099 				if (!info) {
19100 					info = {"Latn":{"nb":215,"nm":"Latin","lid":"Latin","rtl":false,"ime":false,"casing":true}};
19101 					var spec = this.locale.getSpec().replace(/-/g, "_");
19102 					ilib.data.cache.ScriptInfo[spec] = info;
19103 				}
19104 				ilib.data.scripts = info;
19105 				this.info = script && ilib.data.scripts[script];
19106 				if (options && typeof(options.onLoad) === 'function') {
19107 					options.onLoad(this);
19108 				}
19109 			})
19110 		});
19111 	} else {
19112 		this.info = ilib.data.scripts[script];
19113         if (options && typeof(options.onLoad) === 'function') {
19114             options.onLoad(this);
19115         }
19116 	}
19117 };
19118 
19119 /**
19120  * @private
19121  */
19122 ScriptInfo._getScriptsArray = function() {
19123 	var ret = [],
19124 		script = undefined,
19125 		scripts = ilib.data.scripts;
19126 
19127 	for (script in scripts) {
19128 		if (script && scripts[script]) {
19129 			ret.push(script);
19130 		}
19131 	}
19132 	
19133 	return ret;
19134 };
19135 
19136 /**
19137  * Return an array of all ISO 15924 4-letter identifier script identifiers that
19138  * this copy of ilib knows about.
19139  * @static
19140  * @param {boolean} sync whether to find the available ids synchronously (true) or asynchronously (false)
19141  * @param {Object} loadParams arbitrary object full of properties to pass to the loader
19142  * @param {function(Array.<string>)} onLoad callback function to call when the data is finished loading
19143  * @return {Array.<string>} an array of all script identifiers that this copy of
19144  * ilib knows about
19145  */
19146 ScriptInfo.getAllScripts = function(sync, loadParams, onLoad) {
19147 	if (!ilib.data.scripts) {
19148 		Utils.loadData({
19149 			object: "ScriptInfo", 
19150 			locale: "-", 
19151 			name: "scripts.json", 
19152 			sync: sync, 
19153 			loadParams: loadParams, 
19154 			callback: ilib.bind(this, function (info) {
19155 				ilib.data.scripts = info;
19156 				
19157 				if (typeof(onLoad) === 'function') {
19158 					onLoad(ScriptInfo._getScriptsArray());
19159 				}
19160 			})
19161 		});
19162 	} else {
19163         if (typeof(onLoad) === 'function') {
19164             onLoad(ScriptInfo._getScriptsArray());
19165         }
19166 	}
19167 	
19168 	return ScriptInfo._getScriptsArray();
19169 };
19170 
19171 ScriptInfo.prototype = {
19172 	/**
19173 	 * Return the 4-letter ISO 15924 identifier associated
19174 	 * with this script.
19175 	 * @return {string} the 4-letter ISO code for this script
19176 	 */
19177 	getCode: function () {
19178 		return this.info && this.script;
19179 	},
19180 	
19181 	/**
19182 	 * Get the ISO 15924 code number associated with this
19183 	 * script.
19184 	 * 
19185 	 * @return {number} the ISO 15924 code number
19186 	 */
19187 	getCodeNumber: function () {
19188 		return this.info && this.info.nb || 0;
19189 	},
19190 	
19191 	/**
19192 	 * Get the name of this script in English.
19193 	 * 
19194 	 * @return {string} the name of this script in English
19195 	 */
19196 	getName: function () {
19197 		return this.info && this.info.nm;
19198 	},
19199 	
19200 	/**
19201 	 * Get the long identifier assciated with this script.
19202 	 * 
19203 	 * @return {string} the long identifier of this script
19204 	 */
19205 	getLongCode: function () {
19206 		return this.info && this.info.lid;
19207 	},
19208 	
19209 	/**
19210 	 * Return the usual direction that text in this script is written
19211 	 * in. Possible return values are "rtl" for right-to-left,
19212 	 * "ltr" for left-to-right, and "ttb" for top-to-bottom.
19213 	 * 
19214 	 * @return {string} the usual direction that text in this script is
19215 	 * written in
19216 	 */
19217 	getScriptDirection: function() {
19218 		return (this.info && typeof(this.info.rtl) !== 'undefined' && this.info.rtl) ? "rtl" : "ltr";
19219 	},
19220 	
19221 	/**
19222 	 * Return true if this script typically requires an input method engine
19223 	 * to enter its characters.
19224 	 * 
19225 	 * @return {boolean} true if this script typically requires an IME
19226 	 */
19227 	getNeedsIME: function () {
19228 		return this.info && this.info.ime ? true : false; // converts undefined to false
19229 	},
19230 	
19231 	/**
19232 	 * Return true if this script uses lower- and upper-case characters.
19233 	 * 
19234 	 * @return {boolean} true if this script uses letter case
19235 	 */
19236 	getCasing: function () {
19237 		return this.info && this.info.casing ? true : false; // converts undefined to false
19238 	}
19239 };
19240 
19241 
19242 /*< DurationFmt.js */
19243 /*
19244  * DurFmt.js - Date formatter definition
19245  *
19246  * Copyright © 2012-2015, 2018, JEDLSoft
19247  *
19248  * Licensed under the Apache License, Version 2.0 (the "License");
19249  * you may not use this file except in compliance with the License.
19250  * You may obtain a copy of the License at
19251  *
19252  *     http://www.apache.org/licenses/LICENSE-2.0
19253  *
19254  * Unless required by applicable law or agreed to in writing, software
19255  * distributed under the License is distributed on an "AS IS" BASIS,
19256  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19257  *
19258  * See the License for the specific language governing permissions and
19259  * limitations under the License.
19260  */
19261 
19262 /*
19263 !depends 
19264 ilib.js 
19265 Locale.js 
19266 DateFmt.js
19267 IString.js 
19268 ResBundle.js 
19269 LocaleInfo.js
19270 JSUtils.js
19271 ScriptInfo.js
19272 */
19273 
19274 // !data dateformats sysres
19275 // !resbundle sysres
19276 
19277 
19278 /**
19279  * @class
19280  * Create a new duration formatter instance. The duration formatter is immutable once
19281  * it is created, but can format as many different durations as needed with the same
19282  * options. Create different duration formatter instances for different purposes
19283  * and then keep them cached for use later if you have more than one duration to
19284  * format.<p>
19285  * 
19286  * Duration formatters format lengths of time. The duration formatter is meant to format 
19287  * durations of such things as the length of a song or a movie or a meeting, or the 
19288  * current position in that song or movie while playing it. If you wish to format a 
19289  * period of time that has a specific start and end date/time, then use a
19290  * [DateRngFmt] instance instead and call its format method.<p>
19291  *  
19292  * The options may contain any of the following properties:
19293  * 
19294  * <ul>
19295  * <li><i>locale</i> - locale to use when formatting the duration. If the locale is
19296  * not specified, then the default locale of the app or web page will be used.
19297  * 
19298  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 
19299  * formatted string.
19300  * 
19301  * <ul>
19302  * <li><i>short</i> - use a short representation of the duration. This is the most compact format possible for the locale. eg. 1y 1m 1w 1d 1:01:01
19303  * <li><i>medium</i> - use a medium length representation of the duration. This is a slightly longer format. eg. 1 yr 1 mo 1 wk 1 dy 1 hr 1 mi 1 se
19304  * <li><i>long</i> - use a long representation of the duration. This is a fully specified format, but some of the textual 
19305  * parts may still be abbreviated. eg. 1 yr 1 mo 1 wk 1 day 1 hr 1 min 1 sec
19306  * <li><i>full</i> - use a full representation of the duration. This is a fully specified format where all the textual 
19307  * parts are spelled out completely. eg. 1 year, 1 month, 1 week, 1 day, 1 hour, 1 minute and 1 second
19308  * </ul>
19309  * 
19310  * <li><i>style<i> - whether hours, minutes, and seconds should be formatted as a text string
19311  * or as a regular time as on a clock. eg. text is "1 hour, 15 minutes", whereas clock is "1:15:00". Valid
19312  * values for this property are "text" or "clock". Default if this property is not specified
19313  * is "text".
19314  * 
19315  *<li><i>useNative</i> - the flag used to determaine whether to use the native script settings 
19316  * for formatting the numbers .
19317  * 
19318  * <li><i>onLoad</i> - a callback function to call when the format data is fully 
19319  * loaded. When the onLoad option is given, this class will attempt to
19320  * load any missing locale data using the ilib loader callback.
19321  * When the constructor is done (even if the data is already preassembled), the 
19322  * onLoad function is called with the current instance as a parameter, so this
19323  * callback can be used with preassembled or dynamic loading or a mix of the two. 
19324  * 
19325  * <li>sync - tell whether to load any missing locale data synchronously or 
19326  * asynchronously. If this option is given as "false", then the "onLoad"
19327  * callback must be given, as the instance returned from this constructor will
19328  * not be usable for a while.
19329  *  
19330  * <li><i>loadParams</i> - an object containing parameters to pass to the 
19331  * loader callback function when locale data is missing. The parameters are not
19332  * interpretted or modified in any way. They are simply passed along. The object 
19333  * may contain any property/value pairs as long as the calling code is in
19334  * agreement with the loader callback function as to what those parameters mean.
19335  * </ul>
19336  * <p>
19337  * 
19338  * 
19339  * @constructor
19340  * @param {?Object} options options governing the way this date formatter instance works
19341  */
19342 var DurationFmt = function(options) {
19343 	var sync = true;
19344 	var loadParams = undefined;
19345 	
19346 	this.locale = new Locale();
19347 	this.length = "short";
19348 	this.style = "text";
19349 	
19350 	if (options) {
19351 		if (options.locale) {
19352 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
19353 		}
19354 		
19355 		if (options.length) {
19356 			if (options.length === 'short' ||
19357 				options.length === 'medium' ||
19358 				options.length === 'long' ||
19359 				options.length === 'full') {
19360 				this.length = options.length;
19361 			}
19362 		}
19363 		
19364 		if (options.style) {
19365 			if (options.style === 'text' || options.style === 'clock') {
19366 				this.style = options.style;
19367 			}
19368 		}
19369 		
19370 		if (typeof(options.sync) !== 'undefined') {
19371 			sync = !!options.sync;
19372 		}
19373 		
19374 		if (typeof(options.useNative) === 'boolean') {
19375 			this.useNative = options.useNative;
19376 		}
19377 		
19378 		loadParams = options.loadParams;
19379 	}
19380 	options = options || {sync: true};
19381 
19382     new LocaleInfo(this.locale, {
19383         sync: sync,
19384         loadParams: loadParams,
19385         onLoad: ilib.bind(this, function (li) {
19386             this.script = li.getScript();
19387             new ResBundle({
19388                 locale: this.locale,
19389                 name: "sysres",
19390                 sync: sync,
19391                 loadParams: loadParams,
19392                 onLoad: ilib.bind(this, function (sysres) {
19393                     IString.loadPlurals(sync, this.locale, loadParams, ilib.bind(this, function() {
19394                         if (this.length === 'medium' && !(this.script === 'Latn' || this.script ==='Grek' || this.script ==='Cyrl')) {
19395                             this.length = 'short';
19396                         }
19397                         switch (this.length) {
19398                             case 'short':
19399                                 this.components = {
19400                                 year: sysres.getString("#{num}y"),
19401                                 month: sysres.getString("#{num}m", "durationShortMonths"),
19402                                 week: sysres.getString("#{num}w"),
19403                                 day: sysres.getString("#{num}d"),
19404                                 hour: sysres.getString("#{num}h"),
19405                                 minute: sysres.getString("#{num}m", "durationShortMinutes"),
19406                                 second: sysres.getString("#{num}s"),
19407                                 millisecond: sysres.getString("#{num}m", "durationShortMillis"),
19408                                 separator: sysres.getString(" ", "separatorShort"),
19409                                 finalSeparator: "" // not used at this length
19410                             };
19411                                 break;
19412 
19413                             case 'medium':
19414                                 this.components = {
19415                                 year: sysres.getString("1#1 yr|#{num} yrs", "durationMediumYears"),
19416                                 month: sysres.getString("1#1 mo|#{num} mos"),
19417                                 week: sysres.getString("1#1 wk|#{num} wks", "durationMediumWeeks"),
19418                                 day: sysres.getString("1#1 dy|#{num} dys"),
19419                                 hour: sysres.getString("1#1 hr|#{num} hrs", "durationMediumHours"),
19420                                 minute: sysres.getString("1#1 mi|#{num} min"),
19421                                 second: sysres.getString("1#1 se|#{num} sec"),
19422                                 millisecond: sysres.getString("#{num} ms", "durationMediumMillis"),
19423                                 separator: sysres.getString(" ", "separatorMedium"),
19424                                 finalSeparator: "" // not used at this length
19425                             };
19426                                 break;
19427 
19428                             case 'long':
19429                                 this.components = {
19430                                 year: sysres.getString("1#1 yr|#{num} yrs"),
19431                                 month: sysres.getString("1#1 mon|#{num} mons"),
19432                                 week: sysres.getString("1#1 wk|#{num} wks"),
19433                                 day: sysres.getString("1#1 day|#{num} days", "durationLongDays"),
19434                                 hour: sysres.getString("1#1 hr|#{num} hrs"),
19435                                 minute: sysres.getString("1#1 min|#{num} min"),
19436                                 second: sysres.getString("1#1 sec|#{num} sec"),
19437                                 millisecond: sysres.getString("#{num} ms"),
19438                                 separator: sysres.getString(", ", "separatorLong"),
19439                                 finalSeparator: "" // not used at this length
19440                             };
19441                                 break;
19442 
19443                             case 'full':
19444                                 this.components = {
19445                                 year: sysres.getString("1#1 year|#{num} years"),
19446                                 month: sysres.getString("1#1 month|#{num} months"),
19447                                 week: sysres.getString("1#1 week|#{num} weeks"),
19448                                 day: sysres.getString("1#1 day|#{num} days"),
19449                                 hour: sysres.getString("1#1 hour|#{num} hours"),
19450                                 minute: sysres.getString("1#1 minute|#{num} minutes"),
19451                                 second: sysres.getString("1#1 second|#{num} seconds"),
19452                                 millisecond: sysres.getString("1#1 millisecond|#{num} milliseconds"),
19453                                 separator: sysres.getString(", ", "separatorFull"),
19454                                 finalSeparator: sysres.getString(" and ", "finalSeparatorFull")
19455                             };
19456                                 break;
19457                         }
19458 
19459                         if (this.style === 'clock') {
19460                             new DateFmt({
19461                                 locale: this.locale,
19462                                 calendar: "gregorian",
19463                                 type: "time",
19464                                 time: "ms",
19465                                 sync: sync,
19466                                 loadParams: loadParams,
19467                                 useNative: this.useNative,
19468                                 onLoad: ilib.bind(this, function (fmtMS) {
19469                                     this.timeFmtMS = fmtMS;
19470                                     new DateFmt({
19471                                         locale: this.locale,
19472                                         calendar: "gregorian",
19473                                         type: "time",
19474                                         time: "hm",
19475                                         sync: sync,
19476                                         loadParams: loadParams,
19477                                         useNative: this.useNative,
19478                                         onLoad: ilib.bind(this, function (fmtHM) {
19479                                             this.timeFmtHM = fmtHM;
19480                                             new DateFmt({
19481                                                 locale: this.locale,
19482                                                 calendar: "gregorian",
19483                                                 type: "time",
19484                                                 time: "hms",
19485                                                 sync: sync,
19486                                                 loadParams: loadParams,
19487                                                 useNative: this.useNative,
19488                                                 onLoad: ilib.bind(this, function (fmtHMS) {
19489                                                     this.timeFmtHMS = fmtHMS;
19490 
19491                                                     // munge with the template to make sure that the hours are not formatted mod 12
19492                                                     this.timeFmtHM.template = this.timeFmtHM.template.replace(/hh?/, 'H');
19493                                                     this.timeFmtHM.templateArr = this.timeFmtHM._tokenize(this.timeFmtHM.template);
19494                                                     this.timeFmtHMS.template = this.timeFmtHMS.template.replace(/hh?/, 'H');
19495                                                     this.timeFmtHMS.templateArr = this.timeFmtHMS._tokenize(this.timeFmtHMS.template);
19496 
19497                                                     this._init(this.timeFmtHM.locinfo, options);
19498                                                 })
19499                                             });
19500                                         })
19501                                     });
19502                                 })
19503                             });
19504                             return;
19505                         }
19506                         this._init(li, options);
19507                     }));
19508                 })
19509             });
19510         })
19511     });
19512 };
19513 
19514 /**
19515  * @private
19516  * @static
19517  */
19518 DurationFmt.complist = {
19519 	"text": ["year", "month", "week", "day", "hour", "minute", "second", "millisecond"],
19520 	"clock": ["year", "month", "week", "day"]
19521 };
19522 
19523 /**
19524  * @private
19525  */
19526 DurationFmt.prototype._mapDigits = function(str) {
19527 	if (this.useNative && this.digits) {
19528 		return JSUtils.mapString(str.toString(), this.digits);
19529 	}
19530 	return str;
19531 };
19532 
19533 /**
19534  * @private
19535  * @param {LocaleInfo} locinfo
19536  * @param {Object|undefined} options
19537  */
19538 DurationFmt.prototype._init = function(locinfo, options) {
19539     var digits;
19540     new ScriptInfo(locinfo.getScript(), {
19541         sync: options.sync,
19542         loadParams: options.loadParams,
19543         onLoad: ilib.bind(this, function(scriptInfo) {
19544             this.scriptDirection = scriptInfo.getScriptDirection();
19545 
19546             if (typeof(this.useNative) === 'boolean') {
19547                 // if the caller explicitly said to use native or not, honour that despite what the locale data says...
19548                 if (this.useNative) {
19549                     digits = locinfo.getNativeDigits();
19550                     if (digits) {
19551                         this.digits = digits;
19552                     }
19553                 }
19554             } else if (locinfo.getDigitsStyle() === "native") {
19555                 // else if the locale usually uses native digits, then use them 
19556                 digits = locinfo.getNativeDigits();
19557                 if (digits) {
19558                     this.useNative = true;
19559                     this.digits = digits;
19560                 }
19561             } // else use western digits always
19562 
19563             if (typeof(options.onLoad) === 'function') {
19564                 options.onLoad(this);
19565             }      
19566         })
19567     });
19568 };
19569 
19570 /**
19571  * Format a duration according to the format template of this formatter instance.<p>
19572  * 
19573  * The components parameter should be an object that contains any or all of these 
19574  * numeric properties:
19575  * 
19576  * <ul>
19577  * <li>year
19578  * <li>month
19579  * <li>week
19580  * <li>day
19581  * <li>hour
19582  * <li>minute
19583  * <li>second
19584  * </ul>
19585  * <p>
19586  *
19587  * When a property is left out of the components parameter or has a value of 0, it will not
19588  * be formatted into the output string, except for times that include 0 minutes and 0 seconds.
19589  * 
19590  * This formatter will not ensure that numbers for each component property is within the
19591  * valid range for that component. This allows you to format durations that are longer
19592  * than normal range. For example, you could format a duration has being "33 hours" rather
19593  * than "1 day, 9 hours".
19594  * 
19595  * @param {Object} components date/time components to be formatted into a duration string
19596  * @return {IString} a string with the duration formatted according to the style and 
19597  * locale set up for this formatter instance. If the components parameter is empty or 
19598  * undefined, an empty string is returned.
19599  */
19600 DurationFmt.prototype.format = function (components) {
19601 	var i, list, fmt, secondlast = true, str = "";
19602 	
19603 	list = DurationFmt.complist[this.style];
19604 	//for (i = 0; i < list.length; i++) {
19605 	for (i = list.length-1; i >= 0; i--) {
19606 		//console.log("Now dealing with " + list[i]);
19607 		if (typeof(components[list[i]]) !== 'undefined' && components[list[i]] != 0) {
19608 			if (str.length > 0) {
19609 				str = ((this.length === 'full' && secondlast) ? this.components.finalSeparator : this.components.separator) + str;
19610 				secondlast = false;
19611 			}
19612 			str = this.components[list[i]].formatChoice(components[list[i]], {num: this._mapDigits(components[list[i]])}) + str;
19613 		}
19614 	}
19615 
19616 	if (this.style === 'clock') {
19617 		if (typeof(components.hour) !== 'undefined') {
19618 			fmt = (typeof(components.second) !== 'undefined') ? this.timeFmtHMS : this.timeFmtHM;
19619 		} else {
19620 			fmt = this.timeFmtMS;
19621 		}
19622 				
19623 		if (str.length > 0) {
19624 			str += this.components.separator;
19625 		}
19626 		str += fmt._formatTemplate(components, fmt.templateArr);
19627 	}
19628 
19629 	if (this.scriptDirection === 'rtl') {
19630 		str = "\u200F" + str;
19631 	}
19632 	return new IString(str);
19633 };
19634 
19635 /**
19636  * Return the locale that was used to construct this duration formatter object. If the
19637  * locale was not given as parameter to the constructor, this method returns the default
19638  * locale of the system.
19639  * 
19640  * @return {Locale} locale that this duration formatter was constructed with
19641  */
19642 DurationFmt.prototype.getLocale = function () {
19643 	return this.locale;
19644 };
19645 
19646 /**
19647  * Return the length that was used to construct this duration formatter object. If the
19648  * length was not given as parameter to the constructor, this method returns the default
19649  * length. Valid values are "short", "medium", "long", and "full".
19650  * 
19651  * @return {string} length that this duration formatter was constructed with
19652  */
19653 DurationFmt.prototype.getLength = function () {
19654 	return this.length;
19655 };
19656 
19657 /**
19658  * Return the style that was used to construct this duration formatter object. Returns
19659  * one of "text" or "clock".
19660  * 
19661  * @return {string} style that this duration formatter was constructed with
19662  */
19663 DurationFmt.prototype.getStyle = function () {
19664 	return this.style;
19665 };
19666 
19667 
19668 
19669 /*< isAlpha.js */
19670 /*
19671  * ctype.islpha.js - Character type is alphabetic
19672  *
19673  * Copyright © 2012-2015, JEDLSoft
19674  *
19675  * Licensed under the Apache License, Version 2.0 (the "License");
19676  * you may not use this file except in compliance with the License.
19677  * You may obtain a copy of the License at
19678  *
19679  *     http://www.apache.org/licenses/LICENSE-2.0
19680  *
19681  * Unless required by applicable law or agreed to in writing, software
19682  * distributed under the License is distributed on an "AS IS" BASIS,
19683  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19684  *
19685  * See the License for the specific language governing permissions and
19686  * limitations under the License.
19687  */
19688 
19689 // !depends CType.js IString.js ilib.js
19690 
19691 // !data ctype_l
19692 
19693 
19694 /**
19695  * Return whether or not the first character is alphabetic.<p>
19696  *
19697  * @static
19698  * @param {string|IString|number} ch character or code point to examine
19699  * @return {boolean} true if the first character is alphabetic.
19700  */
19701 var isAlpha = function (ch) {
19702 	var num;
19703 	switch (typeof(ch)) {
19704 		case 'number':
19705 			num = ch;
19706 			break;
19707 		case 'string':
19708 			num = IString.toCodePoint(ch, 0);
19709 			break;
19710 		case 'undefined':
19711 			return false;
19712 		default:
19713 			num = ch._toCodePoint(0);
19714 			break;
19715 	}
19716 	return ilib.data.ctype_l ?
19717 	    (CType._inRange(num, 'Lu', ilib.data.ctype_l) ||
19718 		CType._inRange(num, 'Ll', ilib.data.ctype_l) ||
19719 		CType._inRange(num, 'Lt', ilib.data.ctype_l) ||
19720 		CType._inRange(num, 'Lm', ilib.data.ctype_l) ||
19721 		CType._inRange(num, 'Lo', ilib.data.ctype_l)) :
19722 		((num >= 0x41 && num <= 0x5A) || (num >= 0x61 && num <= 0x7A));
19723 };
19724 
19725 /**
19726  * @protected
19727  * @param {boolean} sync
19728  * @param {Object|undefined} loadParams
19729  * @param {function(*)|undefined} onLoad
19730  */
19731 isAlpha._init = function (sync, loadParams, onLoad) {
19732 	CType._load("ctype_l", sync, loadParams, onLoad);
19733 };
19734 
19735 
19736 /*< isAlnum.js */
19737 /*
19738  * isAlnum.js - Character type is alphanumeric
19739  * 
19740  * Copyright © 2012-2015, JEDLSoft
19741  *
19742  * Licensed under the Apache License, Version 2.0 (the "License");
19743  * you may not use this file except in compliance with the License.
19744  * You may obtain a copy of the License at
19745  *
19746  *     http://www.apache.org/licenses/LICENSE-2.0
19747  *
19748  * Unless required by applicable law or agreed to in writing, software
19749  * distributed under the License is distributed on an "AS IS" BASIS,
19750  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19751  *
19752  * See the License for the specific language governing permissions and
19753  * limitations under the License.
19754  */
19755 
19756 // !depends IString.js isAlpha.js isDigit.js
19757 
19758 
19759 /**
19760  * Return whether or not the first character is alphabetic or numeric.<p>
19761  * 
19762  * @static
19763  * @param {string|IString|number} ch character or code point to examine
19764  * @return {boolean} true if the first character is alphabetic or numeric
19765  */
19766 var isAlnum = function (ch) {
19767 	var num;
19768 	switch (typeof(ch)) {
19769 		case 'number':
19770 			num = ch;
19771 			break;
19772 		case 'string':
19773 			num = IString.toCodePoint(ch, 0);
19774 			break;
19775 		case 'undefined':
19776 			return false;
19777 		default:
19778 			num = ch._toCodePoint(0);
19779 			break;
19780 	}
19781 	return isAlpha(num) || isDigit(num);
19782 };
19783 
19784 /**
19785  * @protected
19786  * @param {boolean} sync
19787  * @param {Object|undefined} loadParams
19788  * @param {function(*)|undefined} onLoad
19789  */
19790 isAlnum._init = function (sync, loadParams, onLoad) {
19791 	isAlpha._init(sync, loadParams, function () {
19792 		isDigit._init(sync, loadParams, onLoad);
19793 	});
19794 };
19795 
19796 
19797 
19798 /*< isAscii.js */
19799 /*
19800  * isAscii.js - Character type is ASCII
19801  *
19802  * Copyright © 2012-2015, JEDLSoft
19803  *
19804  * Licensed under the Apache License, Version 2.0 (the "License");
19805  * you may not use this file except in compliance with the License.
19806  * You may obtain a copy of the License at
19807  *
19808  *     http://www.apache.org/licenses/LICENSE-2.0
19809  *
19810  * Unless required by applicable law or agreed to in writing, software
19811  * distributed under the License is distributed on an "AS IS" BASIS,
19812  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19813  *
19814  * See the License for the specific language governing permissions and
19815  * limitations under the License.
19816  */
19817 
19818 // !depends CType.js IString.js ilib.js
19819 
19820 // !data ctype
19821 
19822 
19823 /**
19824  * Return whether or not the first character is in the ASCII range.<p>
19825  *
19826  * @static
19827  * @param {string|IString|number} ch character or code point to examine
19828  * @return {boolean} true if the first character is in the ASCII range.
19829  */
19830 var isAscii = function (ch) {
19831 	var num;
19832 	switch (typeof(ch)) {
19833 		case 'number':
19834 			num = ch;
19835 			break;
19836 		case 'string':
19837 			num = IString.toCodePoint(ch, 0);
19838 			break;
19839 		case 'undefined':
19840 			return false;
19841 		default:
19842 			num = ch._toCodePoint(0);
19843 			break;
19844 	}
19845 	return ilib.data.ctype ? CType._inRange(num, 'ascii', ilib.data.ctype) : (num <= 0x7F);
19846 };
19847 
19848 /**
19849  * @protected
19850  * @param {boolean} sync
19851  * @param {Object|undefined} loadParams
19852  * @param {function(*)|undefined} onLoad
19853  */
19854 isAscii._init = function (sync, loadParams, onLoad) {
19855 	CType._init(sync, loadParams, onLoad);
19856 };
19857 
19858 
19859 /*< isBlank.js */
19860 /*
19861  * isBlank.js - Character type is blank
19862  *
19863  * Copyright © 2012-2015, JEDLSoft
19864  *
19865  * Licensed under the Apache License, Version 2.0 (the "License");
19866  * you may not use this file except in compliance with the License.
19867  * You may obtain a copy of the License at
19868  *
19869  *     http://www.apache.org/licenses/LICENSE-2.0
19870  *
19871  * Unless required by applicable law or agreed to in writing, software
19872  * distributed under the License is distributed on an "AS IS" BASIS,
19873  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19874  *
19875  * See the License for the specific language governing permissions and
19876  * limitations under the License.
19877  */
19878 
19879 // !depends CType.js IString.js ilib.js
19880 
19881 // !data ctype
19882 
19883 
19884 /**
19885  * Return whether or not the first character is a blank character.<p>
19886  *
19887  * @static
19888  * ie. a space or a tab.
19889  * @param {string|IString|number} ch character or code point to examine
19890  * @return {boolean} true if the first character is a blank character.
19891  */
19892 var isBlank = function (ch) {
19893 	var num;
19894 	switch (typeof(ch)) {
19895 		case 'number':
19896 			num = ch;
19897 			break;
19898 		case 'string':
19899 			num = IString.toCodePoint(ch, 0);
19900 			break;
19901 		case 'undefined':
19902 			return false;
19903 		default:
19904 			num = ch._toCodePoint(0);
19905 			break;
19906 	}
19907 	return ilib.data.ctype ? CType._inRange(num, 'blank', ilib.data.ctype) : (ch === ' ' || ch === '\t');
19908 };
19909 
19910 /**
19911  * @protected
19912  * @param {boolean} sync
19913  * @param {Object|undefined} loadParams
19914  * @param {function(*)|undefined} onLoad
19915  */
19916 isBlank._init = function (sync, loadParams, onLoad) {
19917 	CType._init(sync, loadParams, onLoad);
19918 };
19919 
19920 
19921 /*< isCntrl.js */
19922 /*
19923  * isCntrl.js - Character type is control character
19924  * 
19925  * Copyright © 2012-2015, JEDLSoft
19926  *
19927  * Licensed under the Apache License, Version 2.0 (the "License");
19928  * you may not use this file except in compliance with the License.
19929  * You may obtain a copy of the License at
19930  *
19931  *     http://www.apache.org/licenses/LICENSE-2.0
19932  *
19933  * Unless required by applicable law or agreed to in writing, software
19934  * distributed under the License is distributed on an "AS IS" BASIS,
19935  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19936  *
19937  * See the License for the specific language governing permissions and
19938  * limitations under the License.
19939  */
19940 
19941 // !depends CType.js IString.js ilib.js
19942 
19943 // !data ctype_c
19944 
19945 
19946 /**
19947  * Return whether or not the first character is a control character.<p>
19948  * 
19949  * @static
19950  * @param {string|IString|number} ch character or code point to examine
19951  * @return {boolean} true if the first character is a control character.
19952  */
19953 var isCntrl = function (ch) {
19954 	var num;
19955 	switch (typeof(ch)) {
19956 		case 'number':
19957 			num = ch;
19958 			break;
19959 		case 'string':
19960 			num = IString.toCodePoint(ch, 0);
19961 			break;
19962 		case 'undefined':
19963 			return false;
19964 		default:
19965 			num = ch._toCodePoint(0);
19966 			break;
19967 	}
19968 	return CType._inRange(num, 'Cc', ilib.data.ctype_c);
19969 };
19970 
19971 /**
19972  * @protected
19973  * @param {boolean} sync
19974  * @param {Object|undefined} loadParams
19975  * @param {function(*)|undefined} onLoad
19976  */
19977 isCntrl._init = function (sync, loadParams, onLoad) {
19978 	CType._load("ctype_c", sync, loadParams, onLoad);
19979 };
19980 
19981 
19982 /*< isGraph.js */
19983 /*
19984  * isGraph.js - Character type is graph char
19985  * 
19986  * Copyright © 2012-2015, JEDLSoft
19987  *
19988  * Licensed under the Apache License, Version 2.0 (the "License");
19989  * you may not use this file except in compliance with the License.
19990  * You may obtain a copy of the License at
19991  *
19992  *     http://www.apache.org/licenses/LICENSE-2.0
19993  *
19994  * Unless required by applicable law or agreed to in writing, software
19995  * distributed under the License is distributed on an "AS IS" BASIS,
19996  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19997  *
19998  * See the License for the specific language governing permissions and
19999  * limitations under the License.
20000  */
20001 
20002 // !depends IString.js isSpace.js isCntrl.js
20003 
20004 
20005 /**
20006  * Return whether or not the first character is any printable character
20007  * other than space.<p>
20008  * 
20009  * @static
20010  * @param {string|IString|number} ch character or code point to examine
20011  * @return {boolean} true if the first character is any printable character
20012  * other than space. 
20013  */
20014 var isGraph = function (ch) {
20015 	var num;
20016 	switch (typeof(ch)) {
20017 		case 'number':
20018 			num = ch;
20019 			break;
20020 		case 'string':
20021 			num = IString.toCodePoint(ch, 0);
20022 			break;
20023 		case 'undefined':
20024 			return false;
20025 		default:
20026 			num = ch._toCodePoint(0);
20027 			break;
20028 	}
20029 	return typeof(ch) !== 'undefined' && ch.length > 0 && !isSpace(num) && !isCntrl(num);
20030 };
20031 
20032 /**
20033  * @protected
20034  * @param {boolean} sync
20035  * @param {Object|undefined} loadParams
20036  * @param {function(*)|undefined} onLoad
20037  */
20038 isGraph._init = function (sync, loadParams, onLoad) {
20039 	isSpace._init(sync, loadParams, function () {
20040 		isCntrl._init(sync, loadParams, onLoad);
20041 	});
20042 };
20043 
20044 
20045 
20046 /*< isIdeo.js */
20047 /*
20048  * CType.js - Character type definitions
20049  * 
20050  * Copyright © 2012-2015, JEDLSoft
20051  *
20052  * Licensed under the Apache License, Version 2.0 (the "License");
20053  * you may not use this file except in compliance with the License.
20054  * You may obtain a copy of the License at
20055  *
20056  *     http://www.apache.org/licenses/LICENSE-2.0
20057  *
20058  * Unless required by applicable law or agreed to in writing, software
20059  * distributed under the License is distributed on an "AS IS" BASIS,
20060  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20061  *
20062  * See the License for the specific language governing permissions and
20063  * limitations under the License.
20064  */
20065 
20066 // !depends CType.js IString.js
20067 
20068 // !data ctype
20069 
20070 
20071 /**
20072  * Return whether or not the first character is an ideographic character.<p>
20073  * 
20074  * @static
20075  * @param {string|IString|number} ch character or code point to examine
20076  * @return {boolean} true if the first character is an ideographic character.
20077  */
20078 var isIdeo = function (ch) {
20079 	var num;
20080 	switch (typeof(ch)) {
20081 		case 'number':
20082 			num = ch;
20083 			break;
20084 		case 'string':
20085 			num = IString.toCodePoint(ch, 0);
20086 			break;
20087 		case 'undefined':
20088 			return false;
20089 		default:
20090 			num = ch._toCodePoint(0);
20091 			break;
20092 	}
20093 
20094 	return CType._inRange(num, 'cjk', ilib.data.ctype) ||
20095 		CType._inRange(num, 'cjkradicals', ilib.data.ctype) ||
20096 		CType._inRange(num, 'enclosedcjk', ilib.data.ctype) ||
20097 		CType._inRange(num, 'cjkpunct', ilib.data.ctype) ||
20098 		CType._inRange(num, 'cjkcompatibility', ilib.data.ctype);
20099 };
20100 
20101 /**
20102  * @protected
20103  * @param {boolean} sync
20104  * @param {Object|undefined} loadParams
20105  * @param {function(*)|undefined} onLoad
20106  */
20107 isIdeo._init = function (sync, loadParams, onLoad) {
20108 	CType._init(sync, loadParams, onLoad);
20109 };
20110 
20111 
20112 /*< isLower.js */
20113 /*
20114  * isLower.js - Character type is lower case letter
20115  *
20116  * Copyright © 2012-2015, JEDLSoft
20117  *
20118  * Licensed under the Apache License, Version 2.0 (the "License");
20119  * you may not use this file except in compliance with the License.
20120  * You may obtain a copy of the License at
20121  *
20122  *     http://www.apache.org/licenses/LICENSE-2.0
20123  *
20124  * Unless required by applicable law or agreed to in writing, software
20125  * distributed under the License is distributed on an "AS IS" BASIS,
20126  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20127  *
20128  * See the License for the specific language governing permissions and
20129  * limitations under the License.
20130  */
20131 
20132 // !depends CType.js IString.js
20133 
20134 // !data ctype_l
20135 
20136 
20137 
20138 /**
20139  * Return whether or not the first character is lower-case. For alphabetic
20140  * characters in scripts that do not make a distinction between upper- and
20141  * lower-case, this function always returns true.<p>
20142  *
20143  * @static
20144  * @param {string|IString|number} ch character or code point to examine
20145  * @return {boolean} true if the first character is lower-case.
20146  */
20147 var isLower = function (ch) {
20148 	var num;
20149 	switch (typeof(ch)) {
20150 		case 'number':
20151 			num = ch;
20152 			break;
20153 		case 'string':
20154 			num = IString.toCodePoint(ch, 0);
20155 			break;
20156 		case 'undefined':
20157 			return false;
20158 		default:
20159 			num = ch._toCodePoint(0);
20160 			break;
20161 	}
20162 
20163 	return ilib.data.ctype_l ? CType._inRange(num, 'Ll', ilib.data.ctype_l) : (num >= 0x61 && num <= 0x7A);
20164 };
20165 
20166 /**
20167  * @protected
20168  * @param {boolean} sync
20169  * @param {Object|undefined} loadParams
20170  * @param {function(*)|undefined} onLoad
20171  */
20172 isLower._init = function (sync, loadParams, onLoad) {
20173 	CType._load("ctype_l", sync, loadParams, onLoad);
20174 };
20175 
20176 
20177 /*< isPrint.js */
20178 /*
20179  * isPrint.js - Character type is printable char
20180  * 
20181  * Copyright © 2012-2015, JEDLSoft
20182  *
20183  * Licensed under the Apache License, Version 2.0 (the "License");
20184  * you may not use this file except in compliance with the License.
20185  * You may obtain a copy of the License at
20186  *
20187  *     http://www.apache.org/licenses/LICENSE-2.0
20188  *
20189  * Unless required by applicable law or agreed to in writing, software
20190  * distributed under the License is distributed on an "AS IS" BASIS,
20191  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20192  *
20193  * See the License for the specific language governing permissions and
20194  * limitations under the License.
20195  */
20196 
20197 // !depends isCntrl.js
20198 
20199 
20200 /**
20201  * Return whether or not the first character is any printable character,
20202  * including space.<p>
20203  * 
20204  * @static
20205  * @param {string|IString|number} ch character or code point to examine
20206  * @return {boolean} true if the first character is printable.
20207  */
20208 var isPrint = function (ch) {
20209 	return typeof(ch) !== 'undefined' && ch.length > 0 && !isCntrl(ch);
20210 };
20211 
20212 /**
20213  * @protected
20214  * @param {boolean} sync
20215  * @param {Object|undefined} loadParams
20216  * @param {function(*)|undefined} onLoad
20217  */
20218 isPrint._init = function (sync, loadParams, onLoad) {
20219 	isCntrl._init(sync, loadParams, onLoad);
20220 };
20221 
20222 
20223 /*< isPunct.js */
20224 /*
20225  * isPunct.js - Character type is punctuation
20226  *
20227  * Copyright © 2012-2015, JEDLSoft
20228  *
20229  * Licensed under the Apache License, Version 2.0 (the "License");
20230  * you may not use this file except in compliance with the License.
20231  * You may obtain a copy of the License at
20232  *
20233  *     http://www.apache.org/licenses/LICENSE-2.0
20234  *
20235  * Unless required by applicable law or agreed to in writing, software
20236  * distributed under the License is distributed on an "AS IS" BASIS,
20237  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20238  *
20239  * See the License for the specific language governing permissions and
20240  * limitations under the License.
20241  */
20242 
20243 // !depends CType.js IString.js
20244 
20245 // !data ctype_p
20246 
20247 
20248 
20249 /**
20250  * Return whether or not the first character is punctuation.<p>
20251  *
20252  * @static
20253  * @param {string|IString|number} ch character or code point to examine
20254  * @return {boolean} true if the first character is punctuation.
20255  */
20256 var isPunct = function (ch) {
20257 	var num;
20258 	switch (typeof(ch)) {
20259 		case 'number':
20260 			num = ch;
20261 			break;
20262 		case 'string':
20263 			num = IString.toCodePoint(ch, 0);
20264 			break;
20265 		case 'undefined':
20266 			return false;
20267 		default:
20268 			num = ch._toCodePoint(0);
20269 			break;
20270 	}
20271 
20272 	return ilib.data.ctype_p ?
20273 	    (CType._inRange(num, 'Pd', ilib.data.ctype_p) ||
20274 		CType._inRange(num, 'Ps', ilib.data.ctype_p) ||
20275 		CType._inRange(num, 'Pe', ilib.data.ctype_p) ||
20276 		CType._inRange(num, 'Pc', ilib.data.ctype_p) ||
20277 		CType._inRange(num, 'Po', ilib.data.ctype_p) ||
20278 		CType._inRange(num, 'Pi', ilib.data.ctype_p) ||
20279 		CType._inRange(num, 'Pf', ilib.data.ctype_p)) :
20280 		((num >= 0x21 && num <= 0x2F) ||
20281 		(num >= 0x3A && num <= 0x40) ||
20282 		(num >= 0x5B && num <= 0x60) ||
20283 		(num >= 0x7B && num <= 0x7E));
20284 };
20285 
20286 /**
20287  * @protected
20288  * @param {boolean} sync
20289  * @param {Object|undefined} loadParams
20290  * @param {function(*)|undefined} onLoad
20291  */
20292 isPunct._init = function (sync, loadParams, onLoad) {
20293 	CType._load("ctype_p", sync, loadParams, onLoad);
20294 };
20295 
20296 
20297 
20298 /*< isUpper.js */
20299 /*
20300  * isUpper.js - Character type is upper-case letter
20301  *
20302  * Copyright © 2012-2015, JEDLSoft
20303  *
20304  * Licensed under the Apache License, Version 2.0 (the "License");
20305  * you may not use this file except in compliance with the License.
20306  * You may obtain a copy of the License at
20307  *
20308  *     http://www.apache.org/licenses/LICENSE-2.0
20309  *
20310  * Unless required by applicable law or agreed to in writing, software
20311  * distributed under the License is distributed on an "AS IS" BASIS,
20312  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20313  *
20314  * See the License for the specific language governing permissions and
20315  * limitations under the License.
20316  */
20317 
20318 // !depends CType.js IString.js
20319 
20320 // !data ctype_l
20321 
20322 
20323 
20324 /**
20325  * Return whether or not the first character is upper-case. For alphabetic
20326  * characters in scripts that do not make a distinction between upper- and
20327  * lower-case, this function always returns true.<p>
20328  *
20329  * @static
20330  * @param {string|IString|number} ch character or code point to examine
20331  * @return {boolean} true if the first character is upper-case.
20332  */
20333 var isUpper = function (ch) {
20334 	var num;
20335 	switch (typeof(ch)) {
20336 		case 'number':
20337 			num = ch;
20338 			break;
20339 		case 'string':
20340 			num = IString.toCodePoint(ch, 0);
20341 			break;
20342 		case 'undefined':
20343 			return false;
20344 		default:
20345 			num = ch._toCodePoint(0);
20346 			break;
20347 	}
20348 
20349 	return ilib.data.ctype_l ? CType._inRange(num, 'Lu', ilib.data.ctype_l) : (num >= 0x41 && num <= 0x5A);
20350 };
20351 
20352 /**
20353  * @protected
20354  * @param {boolean} sync
20355  * @param {Object|undefined} loadParams
20356  * @param {function(*)|undefined} onLoad
20357  */
20358 isUpper._init = function (sync, loadParams, onLoad) {
20359 	CType._load("ctype_l", sync, loadParams, onLoad);
20360 };
20361 
20362 
20363 /*< isXdigit.js */
20364 /*
20365  * isXdigit.js - Character type is hex digit
20366  *
20367  * Copyright © 2012-2015, JEDLSoft
20368  *
20369  * Licensed under the Apache License, Version 2.0 (the "License");
20370  * you may not use this file except in compliance with the License.
20371  * You may obtain a copy of the License at
20372  *
20373  *     http://www.apache.org/licenses/LICENSE-2.0
20374  *
20375  * Unless required by applicable law or agreed to in writing, software
20376  * distributed under the License is distributed on an "AS IS" BASIS,
20377  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20378  *
20379  * See the License for the specific language governing permissions and
20380  * limitations under the License.
20381  */
20382 
20383 // !depends CType.js IString.js
20384 
20385 // !data ctype
20386 
20387 
20388 
20389 /**
20390  * Return whether or not the first character is a hexadecimal digit written
20391  * in the Latin script. (0-9 or A-F)<p>
20392  *
20393  * @static
20394  * @param {string|IString|number} ch character or code point to examine
20395  * @return {boolean} true if the first character is a hexadecimal digit written
20396  * in the Latin script.
20397  */
20398 var isXdigit = function (ch) {
20399 	var num;
20400 	switch (typeof(ch)) {
20401 		case 'number':
20402 			num = ch;
20403 			break;
20404 		case 'string':
20405 			num = IString.toCodePoint(ch, 0);
20406 			break;
20407 		case 'undefined':
20408 			return false;
20409 		default:
20410 			num = ch._toCodePoint(0);
20411 			break;
20412 	}
20413 
20414 	return ilib.data.ctype ? CType._inRange(num, 'xdigit', ilib.data.ctype) :
20415 	    ((num >= 0x30 && num <= 0x39) || (num >= 0x41 && num <= 0x46) || (num >= 0x61 && num <= 0x66));
20416 };
20417 
20418 /**
20419  * @protected
20420  * @param {boolean} sync
20421  * @param {Object|undefined} loadParams
20422  * @param {function(*)|undefined} onLoad
20423  */
20424 isXdigit._init = function (sync, loadParams, onLoad) {
20425 	CType._init(sync, loadParams, onLoad);
20426 };
20427 
20428 
20429 /*< isScript.js */
20430 /*
20431  * isScript.js - Character type is script
20432  * 
20433  * Copyright © 2012-2015, JEDLSoft
20434  *
20435  * Licensed under the Apache License, Version 2.0 (the "License");
20436  * you may not use this file except in compliance with the License.
20437  * You may obtain a copy of the License at
20438  *
20439  *     http://www.apache.org/licenses/LICENSE-2.0
20440  *
20441  * Unless required by applicable law or agreed to in writing, software
20442  * distributed under the License is distributed on an "AS IS" BASIS,
20443  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20444  *
20445  * See the License for the specific language governing permissions and
20446  * limitations under the License.
20447  */
20448 
20449 // !depends CType.js IString.js
20450 
20451 // !data scriptToRange
20452 
20453 
20454 
20455 /**
20456  * Return whether or not the first character in the given string is 
20457  * in the given script. The script is given as the 4-letter ISO
20458  * 15924 script code.<p>
20459  * 
20460  * @static
20461  * @param {string|IString|number} ch character or code point to examine
20462  * @param {string} script the 4-letter ISO 15924 to query against
20463  * @return {boolean} true if the first character is in the given script, and
20464  * false otherwise
20465  */
20466 var isScript = function (ch, script) {
20467 	var num;
20468 	switch (typeof(ch)) {
20469 		case 'number':
20470 			num = ch;
20471 			break;
20472 		case 'string':
20473 			num = IString.toCodePoint(ch, 0);
20474 			break;
20475 		case 'undefined':
20476 			return false;
20477 		default:
20478 			num = ch._toCodePoint(0);
20479 			break;
20480 	}
20481 
20482 	return CType._inRange(num, script, ilib.data.scriptToRange);
20483 };
20484 
20485 /**
20486  * @protected
20487  * @param {boolean} sync
20488  * @param {Object|undefined} loadParams
20489  * @param {function(*)|undefined} onLoad
20490  */
20491 isScript._init = function (sync, loadParams, onLoad) {
20492 	CType._load("scriptToRange", sync, loadParams, onLoad);
20493 };
20494 
20495 
20496 /*< Name.js */
20497 /*
20498  * Name.js - Person name parser
20499  *
20500  * Copyright © 2013-2015, 2018, JEDLSoft
20501  *
20502  * Licensed under the Apache License, Version 2.0 (the "License");
20503  * you may not use this file except in compliance with the License.
20504  * You may obtain a copy of the License at
20505  *
20506  *     http://www.apache.org/licenses/LICENSE-2.0
20507  *
20508  * Unless required by applicable law or agreed to in writing, software
20509  * distributed under the License is distributed on an "AS IS" BASIS,
20510  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20511  *
20512  * See the License for the specific language governing permissions and
20513  * limitations under the License.
20514  */
20515 
20516 /* !depends 
20517 ilib.js 
20518 Locale.js
20519 Utils.js 
20520 isAlpha.js 
20521 isIdeo.js 
20522 isPunct.js 
20523 isSpace.js
20524 JSUtils.js 
20525 IString.js
20526 */
20527 
20528 // !data name
20529 
20530 // notes:
20531 // icelandic given names: http://en.wiktionary.org/wiki/Appendix:Icelandic_given_names
20532 // danish approved given names: http://www.familiestyrelsen.dk/samliv/navne/
20533 // http://www.mentalfloss.com/blogs/archives/59277
20534 // other countries with first name restrictions: Norway, China, New Zealand, Japan, Sweden, Germany, Hungary
20535 
20536 
20537 
20538 /**
20539  * @class
20540  * A class to parse names of people. Different locales have different conventions when it
20541  * comes to naming people.<p>
20542  *
20543  * The options can contain any of the following properties:
20544  *
20545  * <ul>
20546  * <li><i>locale</i> - use the rules and conventions of the given locale in order to parse
20547  * the name
20548  * <li><i>style</i> - explicitly use the named style to parse the name. Valid values so
20549  * far are "western" and "asian". If this property is not specified, then the style will
20550  * be gleaned from the name itself. This class will count the total number of Latin or Asian
20551  * characters. If the majority of the characters are in one style, that style will be
20552  * used to parse the whole name.
20553  * <li><i>order</i> - explicitly use the given order for names. In some locales, such
20554  * as Russian, names may be written equally validly as "givenName familyName" or "familyName
20555  * givenName". This option tells the parser which order to prefer, and overrides the
20556  * default order for the locale. Valid values are "gf" (given-family) or "fg" (family-given).
20557  * <li><i>useSpaces</i> - explicitly specifies whether to use spaces or not between the given name , middle name
20558  * and family name.
20559  * <li><i>compoundFamilyName</i> - for Asian and some other types of names, search for compound
20560  * family names. If this parameter is not specified, the default is to use the setting that is
20561  * most common for the locale. If it is specified, the locale default is 
20562  * overridden with this flag.
20563  * <li>onLoad - a callback function to call when the name info is fully
20564  * loaded and the name has been parsed. When the onLoad option is given, the name object
20565  * will attempt to load any missing locale data using the ilib loader callback.
20566  * When the constructor is done (even if the data is already preassembled), the
20567  * onLoad function is called with the current instance as a parameter, so this
20568  * callback can be used with preassembled or dynamic loading or a mix of the two.
20569  *
20570  * <li>sync - tell whether to load any missing locale data synchronously or
20571  * asynchronously. If this option is given as "false", then the "onLoad"
20572  * callback must be given, as the instance returned from this constructor will
20573  * not be usable for a while.
20574  *
20575  * <li><i>loadParams</i> - an object containing parameters to pass to the
20576  * loader callback function when locale data is missing. The parameters are not
20577  * interpretted or modified in any way. They are simply passed along. The object
20578  * may contain any property/value pairs as long as the calling code is in
20579  * agreement with the loader callback function as to what those parameters mean.
20580  * </ul>
20581  * 
20582  * Additionally, a name instance can be constructed by giving the explicit 
20583  * already-parsed fields or by using another name instance as the parameter. (That is,
20584  * it becomes a copy constructor.) The name fields can be any of the following:
20585  * 
20586  * <ul>
20587  * <li><i>prefix</i> - the prefix prepended to the name
20588  * <li><i>givenName</i> - the person's given or "first" name
20589  * <li><i>middleName</i> - one or more middle names, separated by spaces even if the
20590  * language doesn't use usually use spaces. The spaces are merely separators.
20591  * <li><i>familyName</i> - one or more family or "last" names, separated by spaces 
20592  * even if the language doesn't use usually use spaces. The spaces are merely separators.
20593  * <li><i>suffix</i> - the suffix appended to the name
20594  * <li><i>honorific</i> - the honorific title of the name. This could be formatted
20595  * as a prefix or a suffix, depending on the customs of the particular locale. You
20596  * should only give either an honorific or a prefix/suffix, but not both.
20597  * </ul>
20598  *
20599  * When the parser has completed its parsing, it fills in the same fields as listed 
20600  * above.<p>
20601  *
20602  * For names that include auxilliary words, such as the family name "van der Heijden", all
20603  * of the auxilliary words ("van der") will be included in the field.<p>
20604  *
20605  * For names in some Spanish locales, it is assumed that the family name is doubled. That is,
20606  * a person may have a paternal family name followed by a maternal family name. All
20607  * family names will be listed in the familyName field as normal, separated by spaces.
20608  * When formatting the short version of such names, only the paternal family name is used.
20609  *
20610  * @constructor
20611  * @param {string|Name|Object=} name the name to parse
20612  * @param {Object=} options Options governing the construction of this name instance
20613  */
20614 var Name = function (name, options) {
20615     var sync = true;
20616 
20617     if (!name || name.length === 0) {
20618         if (options && typeof(options.onLoad) === 'function') {
20619             options.onLoad(undefined);
20620         }
20621         return;
20622     }
20623 
20624     this.loadParams = {};
20625 
20626     if (options) {
20627         if (options.locale) {
20628             this.locale = (typeof (options.locale) === 'string') ? new Locale(options.locale) : options.locale;
20629         }
20630 
20631         if (options.style && (options.style === "asian" || options.style === "western")) {
20632             this.style = options.style;
20633         }
20634 
20635         if (options.order && (options.order === "gmf" || options.order === "fmg" || options.order === "fgm")) {
20636             this.order = options.order;
20637         }
20638 
20639         if (typeof(options.sync) === 'boolean') {
20640             sync = options.sync;
20641         }
20642 
20643         if (typeof(options.loadParams) !== 'undefined') {
20644             this.loadParams = options.loadParams;
20645         }
20646         
20647         if (typeof(options.compoundFamilyName) !== 'undefined') {
20648             this.singleFamilyName = !options.compoundFamilyName; 
20649         }
20650     }
20651 
20652     this.locale = this.locale || new Locale();
20653 	
20654 	isAlpha._init(sync, this.loadParams, ilib.bind(this, function() {
20655 		isIdeo._init(sync, this.loadParams, ilib.bind(this, function() {
20656 			isPunct._init(sync, this.loadParams, ilib.bind(this, function() {
20657 				isSpace._init(sync, this.loadParams, ilib.bind(this, function() {
20658 					Utils.loadData({
20659 						object: "Name", 
20660 						locale: this.locale, 
20661 						name: "name.json", 
20662 						sync: sync, 
20663 						loadParams: this.loadParams, 
20664 						callback: ilib.bind(this, function (info) {
20665 							if (!info) {
20666 								info = Name.defaultInfo[this.style || "western"];
20667 								var spec = this.locale.getSpec().replace(/-/g, "_");
20668 								ilib.data.cache.Name[spec] = info;
20669 							}
20670                             if (typeof (name) === 'object') {
20671     							    // copy constructor
20672 							    /**
20673 							     * The prefixes for this name
20674 							     * @type {string|Array.<string>}
20675 							     */
20676 							    this.prefix = name.prefix;
20677 							    /**
20678 							     * The given (personal) name in this name.
20679 							     * @type {string|Array.<string>}
20680 							     */
20681 							    this.givenName = name.givenName;
20682 							    /**
20683 							     * The middle names used in this name. If there are multiple middle names, they all
20684 							     * appear in this field separated by spaces.
20685 							     * @type {string|Array.<string>}
20686 							     */
20687 							    this.middleName = name.middleName;
20688 							    /**
20689 							     * The family names in this name. If there are multiple family names, they all
20690 							     * appear in this field separated by spaces.
20691 							     * @type {string|Array.<string>}
20692 							     */
20693 							    this.familyName = name.familyName;
20694 							    /**
20695 							     * The suffixes for this name. If there are multiple suffixes, they all
20696 							     * appear in this field separated by spaces.
20697 							     * @type {string|Array.<string>}
20698 							     */
20699 							    this.suffix = name.suffix;
20700 							    /**
20701 							     * The honorific title for this name. This honorific will be used as the prefix
20702 							     * or suffix as dictated by the locale
20703 							     * @type {string|Array.<string>}
20704 							     */
20705 							    this.honorific = name.honorific;
20706 
20707 							    // private properties
20708 							    this.locale = name.locale;
20709 							    this.style = name.style;
20710 							    this.order = name.order;
20711 							    this.useSpaces = name.useSpaces;
20712 							    this.isAsianName = name.isAsianName;
20713 							    
20714 							    if (options && typeof(options.onLoad) === 'function') {
20715 	                                options.onLoad(this);
20716 	                            }
20717 							    
20718 					    	        return;
20719 						    }
20720 							/** 
20721 							 * @type {{
20722 							 *   nameStyle:string,
20723 							 *   order:string,
20724 							 *   prefixes:Array.<string>,
20725 							 *   suffixes:Array.<string>,
20726 							 *   auxillaries:Array.<string>,
20727 							 *   honorifics:Array.<string>,
20728 							 *   knownFamilyNames:Array.<string>,
20729 							 *   noCompoundFamilyNames:boolean,
20730 							 *   sortByHeadWord:boolean
20731 							 * }} */
20732 							this.info = info;
20733 							this._init(name);
20734 							if (options && typeof(options.onLoad) === 'function') {
20735 								options.onLoad(this);
20736 							}
20737 						})
20738 					});					
20739 				}));
20740 			}));
20741 		}));
20742 	}));
20743 };
20744 
20745 Name.defaultInfo = {
20746 	"western": ilib.data.name || {
20747 		"components": {
20748 			"short": "gf",
20749 			"medium": "gmf",
20750 			"long": "pgmf",
20751 			"full": "pgmfs",
20752 			"formal_short": "hf",
20753 			"formal_long": "hgf"
20754 		},
20755 		"format": "{prefix} {givenName} {middleName} {familyName}{suffix}",
20756 		"sortByHeadWord": false,
20757 		"nameStyle": "western",
20758 		"conjunctions": {
20759 			"and1": "and",
20760 			"and2": "and",
20761 			"or1": "or",
20762 			"or2": "or"
20763 		},
20764 		"auxillaries": {
20765 			"von": 1,
20766 			"von der": 1,
20767 			"von den": 1,
20768 			"van": 1,
20769 			"van der": 1,
20770 			"van de": 1,
20771 			"van den": 1,
20772 			"de": 1,
20773 			"di": 1,
20774 			"la": 1,
20775 			"lo": 1,
20776 			"des": 1,
20777 			"le": 1,
20778 			"les": 1,
20779 			"du": 1,
20780 			"de la": 1,
20781 			"del": 1,
20782 			"de los": 1,
20783 			"de las": 1
20784 		},
20785 		"prefixes": [
20786 	        "doctor",
20787 	        "dr",
20788 	        "mr",
20789 	        "mrs",
20790 	        "ms",
20791 	        "mister",
20792 	        "madame",
20793 	        "madamoiselle",
20794 	        "miss",
20795 	        "monsieur",
20796 	        "señor",
20797 	        "señora",
20798 	        "señorita"
20799 		],
20800 		"suffixes": [
20801 	        ",",
20802 	        "junior",
20803 	        "jr",
20804 	        "senior",
20805 	        "sr",
20806 	        "i",
20807 	        "ii",
20808 	        "iii",
20809 	        "esq",
20810 	        "phd",
20811 	        "md"
20812 		],
20813 	    "patronymicName":[ ],
20814 	    "familyNames":[ ]
20815 	},
20816 	"asian": {
20817 		"components": {
20818 			"short": "gf",
20819 			"medium": "gmf",
20820 			"long": "hgmf",
20821 			"full": "hgmf",
20822 			"formal_short": "hf",
20823 			"formal_long": "hgf"
20824 		},
20825 		"format": "{prefix}{familyName}{middleName}{givenName}{suffix}",
20826 		"nameStyle": "asian",
20827 		"sortByHeadWord": false,
20828 		"conjunctions": {},
20829 		"auxillaries": {},
20830 		"prefixes": [],
20831 		"suffixes": [],
20832 	    "patronymicName":[],
20833 	    "familyNames":[]
20834 	}
20835 };
20836 
20837 /**
20838  * Return true if the given character is in the range of the Han, Hangul, or kana
20839  * scripts.
20840  * @static
20841  * @protected
20842  */
20843 Name._isAsianChar = function(c) {
20844 	return isIdeo(c) ||
20845 		CType.withinRange(c, "hangul") ||
20846 		CType.withinRange(c, "hiragana") ||
20847 		CType.withinRange(c, "katakana");
20848 };
20849 
20850 
20851 /**
20852  * @static
20853  * @protected
20854  */
20855 Name._isAsianName = function (name, language) {
20856     // the idea is to count the number of asian chars and the number
20857     // of latin chars. If one is greater than the other, choose
20858     // that style.
20859     var asian = 0,
20860         latin = 0,
20861         i;
20862 
20863     if (name && name.length > 0) {
20864         for (i = 0; i < name.length; i++) {
20865         	var c = name.charAt(i);
20866 
20867             if (Name._isAsianChar(c)) {
20868                 if (language =="ko" || language =="ja" || language =="zh") {
20869                     return true;
20870                 }
20871                 asian++;
20872             } else if (isAlpha(c)) {
20873                 if (!language =="ko" || !language =="ja" || !language =="zh") {
20874                     return false;
20875                 }
20876                 latin++;
20877             }
20878         }
20879 
20880         return latin < asian;
20881     }
20882 
20883     return false;
20884 };
20885 
20886 /**
20887  * Return true if any Latin letters are found in the string. Return
20888  * false if all the characters are non-Latin.
20889  * @static
20890  * @protected
20891  */
20892 Name._isEuroName = function (name, language) {
20893     var c,
20894         n = new IString(name),
20895         it = n.charIterator();
20896 
20897     while (it.hasNext()) {
20898         c = it.next();
20899 
20900         if (!Name._isAsianChar(c) && !isPunct(c) && !isSpace(c)) {
20901             return true;
20902         } else if (Name._isAsianChar(c) && (language =="ko" || language =="ja" || language =="zh")) {
20903             return false;
20904         }
20905     }
20906     return false;
20907 };
20908 
20909 Name.prototype = {
20910     /**
20911      * @protected
20912      */
20913     _init: function (name) {
20914         var parts, prefixArray, prefix, prefixLower,
20915             suffixArray, suffix, suffixLower,
20916             i, info, hpSuffix;
20917         var currentLanguage = this.locale.getLanguage();
20918 
20919         if (name) {
20920             // for DFISH-12905, pick off the part that the LDAP server automatically adds to our names in HP emails
20921             i = name.search(/\s*[,\/\(\[\{<]/);
20922             if (i !== -1) {
20923                 hpSuffix = name.substring(i);
20924                 hpSuffix = hpSuffix.replace(/\s+/g, ' '); // compress multiple whitespaces
20925                 suffixArray = hpSuffix.split(" ");
20926                 var conjunctionIndex = this._findLastConjunction(suffixArray);
20927                 if (conjunctionIndex > -1) {
20928                     // it's got conjunctions in it, so this is not really a suffix
20929                     hpSuffix = undefined;
20930                 } else {
20931                     name = name.substring(0, i);
20932                 }
20933             }
20934 
20935             this.isAsianName = Name._isAsianName(name, currentLanguage);
20936             if (this.info.nameStyle === "asian") {
20937                 info = this.isAsianName ? this.info : Name.defaultInfo.western;
20938             } else {
20939                 info = this.isAsianName ? Name.defaultInfo.asian : this.info;
20940             }
20941 
20942             if (this.isAsianName) {
20943                 // all-asian names
20944                 if (this.useSpaces === false) {
20945                     name = name.replace(/\s+/g, ''); // eliminate all whitespaces
20946                 }
20947                 parts = name.trim().split('');
20948             }
20949             //} 
20950             else {
20951                 name = name.replace(/, /g, ' , ');
20952                 name = name.replace(/\s+/g, ' '); // compress multiple whitespaces
20953                 parts = name.trim().split(' ');
20954             }
20955 
20956             // check for prefixes
20957             if (parts.length > 1) {
20958                 for (i = parts.length; i > 0; i--) {
20959                     prefixArray = parts.slice(0, i);
20960                     prefix = prefixArray.join(this.isAsianName ? '' : ' ');
20961                     prefixLower = prefix.toLowerCase();
20962                     prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
20963                     if (ilib.isArray(this.info.prefixes) &&
20964                         (JSUtils.indexOf(this.info.prefixes, prefixLower) > -1 || this._isConjunction(prefixLower))) {
20965                         if (this.prefix) {
20966                             if (!this.isAsianName) {
20967                                 this.prefix += ' ';
20968                             }
20969                             this.prefix += prefix;
20970                         } else {
20971                             this.prefix = prefix;
20972                         }
20973                         parts = parts.slice(i);
20974                         i = parts.length;
20975                     }
20976                 }
20977             }
20978             // check for suffixes
20979             if (parts.length > 1) {
20980                 for (i = parts.length; i > 0; i--) {
20981                     suffixArray = parts.slice(-i);
20982                     suffix = suffixArray.join(this.isAsianName ? '' : ' ');
20983                     suffixLower = suffix.toLowerCase();
20984                     suffixLower = suffixLower.replace(/[\.]/g, ''); // ignore periods
20985                     if (ilib.isArray(this.info.suffixes) && JSUtils.indexOf(this.info.suffixes, suffixLower) > -1) {
20986                         if (this.suffix) {
20987                             if (!this.isAsianName && !isPunct(this.suffix.charAt(0))) {
20988                                 this.suffix = ' ' + this.suffix;
20989                             }
20990                             this.suffix = suffix + this.suffix;
20991                         } else {
20992                             this.suffix = suffix;
20993                         }
20994                         parts = parts.slice(0, parts.length - i);
20995                         i = parts.length;
20996                     }
20997                 }
20998             }
20999 
21000             if (hpSuffix) {
21001                 this.suffix = (this.suffix && this.suffix + hpSuffix) || hpSuffix;
21002             }
21003 
21004             // adjoin auxillary words to their headwords
21005             if (parts.length > 1 && !this.isAsianName) {
21006                 parts = this._joinAuxillaries(parts, this.isAsianName);
21007             }
21008 
21009             if (this.isAsianName) {
21010                 this._parseAsianName(parts, currentLanguage);
21011             } else {
21012                 this._parseWesternName(parts);
21013             }
21014 
21015             this._joinNameArrays();
21016         }
21017     },
21018 
21019     /**
21020 	 * @return {number} 
21021 	 *
21022 	_findSequence: function(parts, hash, isAsian) {
21023 		var sequence, sequenceLower, sequenceArray, aux = [], i, ret = {};
21024 		
21025 		if (parts.length > 0 && hash) {
21026 			//console.info("_findSequence: finding sequences");
21027 			for (var start = 0; start < parts.length-1; start++) {
21028 				for ( i = parts.length; i > start; i-- ) {
21029 					sequenceArray = parts.slice(start, i);
21030 					sequence = sequenceArray.join(isAsian ? '' : ' ');
21031 					sequenceLower = sequence.toLowerCase();
21032 					sequenceLower = sequenceLower.replace(/[,\.]/g, '');  // ignore commas and periods
21033 					
21034 					//console.info("_findSequence: checking sequence: '" + sequenceLower + "'");
21035 					
21036 					if ( sequenceLower in hash ) {
21037 						ret.match = sequenceArray;
21038 						ret.start = start;
21039 						ret.end = i;
21040 						return ret;
21041 						//console.info("_findSequence: Found sequence '" + sequence + "' New parts list is " + JSON.stringify(parts));
21042 					}
21043 				}
21044 			}
21045 		}
21046 	
21047 		return undefined;
21048 	},
21049 	*/
21050 
21051     /**
21052      * @protected
21053      * @param {Array} parts
21054      * @param {Array} names
21055      * @param {boolean} isAsian
21056      * @param {boolean=} noCompoundPrefix
21057      */
21058     _findPrefix: function (parts, names, isAsian, noCompoundPrefix) {
21059         var i, prefix, prefixLower, prefixArray, aux = [];
21060 
21061         if (parts.length > 0 && names) {
21062             for (i = parts.length; i > 0; i--) {
21063                 prefixArray = parts.slice(0, i);
21064                 prefix = prefixArray.join(isAsian ? '' : ' ');
21065                 prefixLower = prefix.toLowerCase();
21066                 prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
21067 
21068                 if (prefixLower in names) {
21069                     aux = aux.concat(isAsian ? prefix : prefixArray);
21070                     if (noCompoundPrefix) {
21071                     	// don't need to parse further. Just return it as is.
21072                     	return aux;
21073                     }
21074                     parts = parts.slice(i);
21075                     i = parts.length + 1;
21076                 }
21077             }
21078         }
21079 
21080         return aux;
21081     },
21082 
21083     /**
21084      * @protected
21085      */
21086     _findSuffix: function (parts, names, isAsian) {
21087         var i, j, seq = "";
21088 
21089         for (i = 0; i < names.length; i++) {
21090             if (parts.length >= names[i].length) {
21091                 j = 0;
21092                 while (j < names[i].length && parts[parts.length - j] === names[i][names[i].length - j]) {
21093                     j++;
21094                 }
21095                 if (j >= names[i].length) {
21096                     seq = parts.slice(parts.length - j).join(isAsian ? "" : " ") + (isAsian ? "" : " ") + seq;
21097                     parts = parts.slice(0, parts.length - j);
21098                     i = -1; // restart the search
21099                 }
21100             }
21101         }
21102 
21103         this.suffix = seq;
21104         return parts;
21105     },
21106 
21107     /**
21108      * @protected
21109      * Tell whether or not the given word is a conjunction in this language.
21110      * @param {string} word the word to test
21111      * @return {boolean} true if the word is a conjunction
21112      */
21113     _isConjunction: function _isConjunction(word) {
21114         return (this.info.conjunctions.and1 === word ||
21115             this.info.conjunctions.and2 === word ||
21116             this.info.conjunctions.or1 === word ||
21117             this.info.conjunctions.or2 === word ||
21118             ("&" === word) ||
21119             ("+" === word));
21120     },
21121 
21122     /**
21123      * Find the last instance of 'and' in the name
21124      * @protected
21125      * @param {Array.<string>} parts
21126      * @return {number}
21127      */
21128     _findLastConjunction: function _findLastConjunction(parts) {
21129         var conjunctionIndex = -1,
21130             index, part;
21131 
21132         for (index = 0; index < parts.length; index++) {
21133             part = parts[index];
21134             if (typeof (part) === 'string') {
21135                 part = part.toLowerCase();
21136                 // also recognize English
21137                 if ("and" === part || "or" === part || "&" === part || "+" === part) {
21138                     conjunctionIndex = index;
21139                 }
21140                 if (this._isConjunction(part)) {
21141                     conjunctionIndex = index;
21142                 }
21143             }
21144         }
21145         return conjunctionIndex;
21146     },
21147 
21148     /**
21149      * @protected
21150      * @param {Array.<string>} parts the current array of name parts
21151      * @param {boolean} isAsian true if the name is being parsed as an Asian name
21152      * @return {Array.<string>} the remaining parts after the prefixes have been removed
21153      */
21154     _extractPrefixes: function (parts, isAsian) {
21155         var i = this._findPrefix(parts, this.info.prefixes, isAsian);
21156         if (i > 0) {
21157             this.prefix = parts.slice(0, i).join(isAsian ? "" : " ");
21158             return parts.slice(i);
21159         }
21160         // prefixes not found, so just return the array unmodified
21161         return parts;
21162     },
21163 
21164     /**
21165      * @protected
21166      * @param {Array.<string>} parts the current array of name parts
21167      * @param {boolean} isAsian true if the name is being parsed as an Asian name
21168      * @return {Array.<string>} the remaining parts after the suffices have been removed
21169      */
21170     _extractSuffixes: function (parts, isAsian) {
21171         var i = this._findSuffix(parts, this.info.suffixes, isAsian);
21172         if (i > 0) {
21173             this.suffix = parts.slice(i).join(isAsian ? "" : " ");
21174             return parts.slice(0, i);
21175         }
21176         // suffices not found, so just return the array unmodified
21177         return parts;
21178     },
21179 
21180     /**
21181      * Adjoin auxillary words to their head words.
21182      * @protected
21183      * @param {Array.<string>} parts the current array of name parts
21184      * @param {boolean} isAsian true if the name is being parsed as an Asian name
21185      * @return {Array.<string>} the parts after the auxillary words have been plucked onto their head word
21186      */
21187     _joinAuxillaries: function (parts, isAsian) {
21188         var start, i, prefixArray, prefix, prefixLower;
21189 
21190         if (this.info.auxillaries && (parts.length > 2 || this.prefix)) {
21191             for (start = 0; start < parts.length - 1; start++) {
21192                 for (i = parts.length; i > start; i--) {
21193                     prefixArray = parts.slice(start, i);
21194                     prefix = prefixArray.join(' ');
21195                     prefixLower = prefix.toLowerCase();
21196                     prefixLower = prefixLower.replace(/[,\.]/g, ''); // ignore commas and periods
21197 
21198                     if (prefixLower in this.info.auxillaries) {
21199                         parts.splice(start, i + 1 - start, prefixArray.concat(parts[i]));
21200                         i = start;
21201                     }
21202                 }
21203             }
21204         }
21205 
21206         return parts;
21207     },
21208 
21209     /**
21210      * Recursively join an array or string into a long string.
21211      * @protected
21212      */
21213     _joinArrayOrString: function _joinArrayOrString(part) {
21214         var i;
21215         if (typeof (part) === 'object') {
21216             for (i = 0; i < part.length; i++) {
21217                 part[i] = this._joinArrayOrString(part[i]);
21218             }
21219             var ret = "";
21220             part.forEach(function (segment) {
21221                 if (ret.length > 0 && !isPunct(segment.charAt(0))) {
21222                     ret += ' ';
21223                 }
21224                 ret += segment;
21225             });
21226 
21227             return ret;
21228         }
21229 
21230         return part;
21231     },
21232 
21233     /**
21234      * @protected
21235      */
21236     _joinNameArrays: function _joinNameArrays() {
21237         var prop;
21238         for (prop in this) {
21239 
21240             if (this[prop] !== undefined && typeof (this[prop]) === 'object' && ilib.isArray(this[prop])) {
21241 
21242                 this[prop] = this._joinArrayOrString(this[prop]);
21243             }
21244         }
21245     },
21246 
21247     /**
21248      * @protected
21249      */
21250     _parseAsianName: function (parts, language) {
21251         var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, typeof(this.singleFamilyName) !== 'undefined' ? this.singleFamilyName : this.info.noCompoundFamilyNames);
21252         var tempFullName = parts.join('');
21253 
21254         if (familyNameArray && familyNameArray.length > 0) {
21255             this.familyName = familyNameArray.join('');
21256             this.givenName = parts.slice(this.familyName.length).join('');
21257             
21258             //Overide parsing rules if spaces are found in korean
21259             if (language === "ko" && tempFullName.search(/\s*[/\s]/) > -1 && !this.suffix) {
21260                 this._parseKoreanName(tempFullName);
21261             }
21262         } else if (this.locale.getLanguage() === "ja") {
21263             this._parseJapaneseName(parts);
21264         } else if (this.suffix || this.prefix) {
21265             this.familyName = parts.join('');
21266         } else {
21267             this.givenName = parts.join('');
21268         }
21269     },
21270 
21271     /**
21272      * @protected
21273      */
21274     _parseKoreanName: function (name) {
21275         var tempName = name;
21276 
21277         var spaceSplit = tempName.split(" ");
21278         var spceCount = spaceSplit.length;
21279         var fistSpaceIndex = tempName.indexOf(" ");
21280         var lastSpaceIndex = tempName.lastIndexOf(" ");
21281 
21282         if (spceCount === 2) {
21283             this.familyName = spaceSplit[0];
21284             this.givenName = tempName.slice(fistSpaceIndex, tempName.length);
21285         } else {
21286             this.familyName = spaceSplit[0];
21287             this.middleName = tempName.slice(fistSpaceIndex, lastSpaceIndex);
21288             this.givenName = tempName.slice(lastSpaceIndex, tempName.length);
21289         }
21290         
21291     },
21292 
21293     /**
21294      * @protected
21295      */
21296     _parseJapaneseName: function (parts) {
21297     	if (this.suffix && this.suffix.length > 1 && this.info.honorifics.indexOf(this.suffix)>-1) {
21298     		if (parts.length === 1) {
21299     			if (CType.withinRange(parts[0], "cjk")) {
21300     				this.familyName = parts[0];
21301     			} else {
21302     				this.givenName = parts[0];
21303     			}
21304     			return;
21305     		} else if (parts.length === 2) {
21306     			this.familyName = parts.slice(0,parts.length).join("")
21307     			return;
21308     		}
21309     	}
21310     	if (parts.length > 1) {
21311     		var fn = "";                                                                    
21312     		for (var i = 0; i < parts.length; i++) {
21313     			if (CType.withinRange(parts[i], "cjk")) {
21314     				fn += parts[i];
21315     			} else if (fn.length > 1 && CType.withinRange(parts[i], "hiragana")) {
21316     				this.familyName = fn;
21317     				this.givenName = parts.slice(i,parts.length).join("");
21318     				return;
21319     			} else {
21320     				break;
21321     			}
21322     		}
21323     	}
21324     	if (parts.length === 1) {
21325     		this.familyName = parts[0];
21326     	} else if (parts.length === 2) {
21327     		this.familyName = parts[0];
21328     		this.givenName = parts[1];
21329     	} else if (parts.length === 3) {
21330     		this.familyName = parts[0];
21331     		this.givenName = parts.slice(1,parts.length).join("");
21332     	} else if (parts.length > 3) {
21333     		this.familyName = parts.slice(0,2).join("")
21334     		this.givenName = parts.slice(2,parts.length).join("");
21335     	}      
21336     },
21337 
21338     /**
21339      * @protected
21340      */
21341     _parseSpanishName: function (parts) {
21342         var conjunctionIndex;
21343 
21344         if (parts.length === 1) {
21345             if (this.prefix || typeof (parts[0]) === 'object') {
21346                 this.familyName = parts[0];
21347             } else {
21348                 this.givenName = parts[0];
21349             }
21350         } else if (parts.length === 2) {
21351             // we do G F
21352             this.givenName = parts[0];
21353             this.familyName = parts[1];
21354         } else if (parts.length === 3) {
21355             conjunctionIndex = this._findLastConjunction(parts);
21356             // if there's an 'and' in the middle spot, put everything in the first name
21357             if (conjunctionIndex === 1) {
21358                 this.givenName = parts;
21359             } else {
21360                 // else, do G F F
21361                 this.givenName = parts[0];
21362                 this.familyName = parts.slice(1);
21363             }
21364         } else if (parts.length > 3) {
21365             //there are at least 4 parts to this name
21366 
21367             conjunctionIndex = this._findLastConjunction(parts);
21368             ////console.log("@@@@@@@@@@@@@@@@"+conjunctionIndex)
21369             if (conjunctionIndex > 0) {
21370                 // if there's a conjunction that's not the first token, put everything up to and 
21371                 // including the token after it into the first name, the last 2 tokens into
21372                 // the family name (if they exist) and everything else in to the middle name
21373                 // 0 1 2 3 4 5
21374                 // G A G
21375                 // G A G F
21376                 // G G A G
21377                 // G A G F F
21378                 // G G A G F
21379                 // G G G A G
21380                 // G A G M F F
21381                 // G G A G F F
21382                 // G G G A G F
21383                 // G G G G A G
21384                 this.givenName = parts.splice(0, conjunctionIndex + 2);
21385                 if (parts.length > 1) {
21386                     this.familyName = parts.splice(parts.length - 2, 2);
21387                     if (parts.length > 0) {
21388                         this.middleName = parts;
21389                     }
21390                 } else if (parts.length === 1) {
21391                     this.familyName = parts[0];
21392                 }
21393             } else {
21394                 this.givenName = parts.splice(0, 1);
21395                 this.familyName = parts.splice(parts.length - 2, 2);
21396                 this.middleName = parts;
21397             }
21398         }
21399     },
21400 
21401     /**
21402      * @protected
21403      */
21404     _parseIndonesianName: function (parts) {
21405         var conjunctionIndex;
21406 
21407         if (parts.length === 1) {
21408             //if (this.prefix || typeof(parts[0]) === 'object') {
21409             //this.familyName = parts[0];
21410             //} else {
21411             this.givenName = parts[0];
21412             //}
21413             //} else if (parts.length === 2) {
21414             // we do G F
21415             //this.givenName = parts[0];
21416             //this.familyName = parts[1];
21417         } else if (parts.length >= 2) {
21418             //there are at least 3 parts to this name
21419 
21420             conjunctionIndex = this._findLastConjunction(parts);
21421             if (conjunctionIndex > 0) {
21422                 // if there's a conjunction that's not the first token, put everything up to and 
21423                 // including the token after it into the first name, the last 2 tokens into
21424                 // the family name (if they exist) and everything else in to the middle name
21425                 // 0 1 2 3 4 5
21426                 // G A G
21427                 // G A G F
21428                 // G G A G
21429                 // G A G F F
21430                 // G G A G F
21431                 // G G G A G
21432                 // G A G M F F
21433                 // G G A G F F
21434                 // G G G A G F
21435                 // G G G G A G
21436                 this.givenName = parts.splice(0, conjunctionIndex + 2);
21437                 if (parts.length > 1) {
21438                     //this.familyName = parts.splice(parts.length-2, 2);
21439                     //if ( parts.length > 0 ) {
21440                     this.middleName = parts;
21441                 }
21442                 //} else if (parts.length === 1) {
21443                 //	this.familyName = parts[0];
21444                 //}
21445             } else {
21446                 this.givenName = parts.splice(0, 1);
21447                 //this.familyName = parts.splice(parts.length-2, 2);
21448                 this.middleName = parts;
21449             }
21450         }
21451     },
21452     
21453     /**
21454      * @protected
21455      */
21456     _parseGenericWesternName: function (parts) {
21457         /* Western names are parsed as follows, and rules are applied in this 
21458          * order:
21459          *
21460          * G
21461          * G F
21462          * G M F
21463          * G M M F
21464          * P F
21465          * P G F
21466          */
21467         var conjunctionIndex;
21468 
21469         if (parts.length === 1) {
21470             if (this.prefix || typeof (parts[0]) === 'object') {
21471                 // already has a prefix, so assume it goes with the family name like "Dr. Roberts" or
21472                 // it is a name with auxillaries, which is almost always a family name
21473                 this.familyName = parts[0];
21474             } else {
21475                 this.givenName = parts[0];
21476             }
21477         } else if (parts.length === 2) {
21478             // we do G F
21479             if (this.info.order == 'fgm') {
21480                 this.givenName = parts[1];
21481                 this.familyName = parts[0];
21482             } else if (this.info.order == "gmf" || typeof (this.info.order) == 'undefined') {
21483                 this.givenName = parts[0];
21484                 this.familyName = parts[1];
21485             }
21486         } else if (parts.length >= 3) {
21487             //find the first instance of 'and' in the name
21488             conjunctionIndex = this._findLastConjunction(parts);
21489 
21490             if (conjunctionIndex > 0) {
21491                 // if there's a conjunction that's not the first token, put everything up to and 
21492                 // including the token after it into the first name, the last token into
21493                 // the family name (if it exists) and everything else in to the middle name
21494                 // 0 1 2 3 4 5
21495                 // G A G M M F
21496                 // G G A G M F
21497                 // G G G A G F
21498                 // G G G G A G
21499                 //if(this.order == "gmf") {
21500                 this.givenName = parts.slice(0, conjunctionIndex + 2);
21501 
21502                 if (conjunctionIndex + 1 < parts.length - 1) {
21503                     this.familyName = parts.splice(parts.length - 1, 1);
21504                     ////console.log(this.familyName);
21505                     if (conjunctionIndex + 2 < parts.length - 1) {
21506                         this.middleName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3);
21507                     }
21508                 } else if (this.info.order == "fgm") {
21509                     this.familyName = parts.slice(0, conjunctionIndex + 2);
21510                     if (conjunctionIndex + 1 < parts.length - 1) {
21511                         this.middleName = parts.splice(parts.length - 1, 1);
21512                         if (conjunctionIndex + 2 < parts.length - 1) {
21513                             this.givenName = parts.slice(conjunctionIndex + 2, parts.length - conjunctionIndex - 3);
21514                         }
21515                     }
21516                 }
21517             } else if (this.info.order === "fgm") {
21518                 this.givenName = parts[1];
21519                 this.middleName = parts.slice(2);
21520                 this.familyName = parts[0];
21521             } else {
21522                 this.givenName = parts[0];
21523                 this.middleName = parts.slice(1, parts.length - 1);
21524                 this.familyName = parts[parts.length - 1];
21525             }
21526         }
21527     },
21528     
21529      /**
21530      * parse patrinomic name from the russian names 
21531      * @protected
21532      * @param {Array.<string>} parts the current array of name parts
21533      * @return number  index of the part which contains patronymic name
21534      */
21535     _findPatronymicName: function(parts) {
21536     	var index, part;
21537     	for (index = 0; index < parts.length; index++) {
21538     		part = parts[index];
21539     		if (typeof (part) === 'string') {
21540     			part = part.toLowerCase();
21541 
21542     			var subLength = this.info.patronymicName.length;
21543     			while(subLength--) {
21544     				if(part.indexOf(this.info.patronymicName[subLength])!== -1 )
21545     					return index;
21546     			}
21547     		}
21548     	}
21549     	return -1;
21550     },
21551 
21552     /**
21553 	 * find if the given part is patronymic name
21554 	 * 
21555 	 * @protected
21556 	 * @param {string} part string from name parts @
21557 	 * @return number index of the part which contains familyName
21558 	 */
21559     _isPatronymicName: function(part) {
21560 	    var pName;
21561 	    if ( typeof (part) === 'string') {
21562 		    pName = part.toLowerCase();
21563 
21564 		    var subLength = this.info.patronymicName.length;
21565 		    while (subLength--) {
21566 			    if (pName.indexOf(this.info.patronymicName[subLength]) !== -1)
21567 				    return true;
21568 		    }
21569 	    }
21570 	    return false;
21571     },
21572 
21573     /**
21574 	 * find family name from the russian name
21575 	 * 
21576 	 * @protected
21577 	 * @param {Array.<string>} parts the current array of name parts
21578 	 * @return boolean true if patronymic, false otherwise
21579 	 */
21580     _findFamilyName: function(parts) {
21581 	    var index, part, substring;
21582 	    for (index = 0; index < parts.length; index++) {
21583 		    part = parts[index];
21584 
21585 		    if ( typeof (part) === 'string') {
21586 			    part = part.toLowerCase();
21587 			    var length = part.length - 1;
21588 
21589 			    if (this.info.familyName.indexOf(part) !== -1) {
21590 				    return index;
21591 			    } else if (part[length] === 'в' || part[length] === 'н' ||
21592 			        part[length] === 'й') {
21593 				    substring = part.slice(0, -1);
21594 				    if (this.info.familyName.indexOf(substring) !== -1) {
21595 					    return index;
21596 				    }
21597 			    } else if ((part[length - 1] === 'в' && part[length] === 'а') ||
21598 			        (part[length - 1] === 'н' && part[length] === 'а') ||
21599 			        (part[length - 1] === 'а' && part[length] === 'я')) {
21600 				    substring = part.slice(0, -2);
21601 				    if (this.info.familyName.indexOf(substring) !== -1) {
21602 					    return index;
21603 				    }
21604 			    }
21605 		    }
21606 	    }
21607 	    return -1;
21608     },
21609 
21610     /**
21611 	 * parse russian name
21612 	 * 
21613 	 * @protected
21614 	 * @param {Array.<string>} parts the current array of name parts
21615 	 * @return
21616 	 */
21617     _parseRussianName: function(parts) {
21618 	    var conjunctionIndex, familyIndex = -1;
21619 
21620 	    if (parts.length === 1) {
21621 		    if (this.prefix || typeof (parts[0]) === 'object') {
21622 			    // already has a prefix, so assume it goes with the family name
21623 				// like "Dr. Roberts" or
21624 			    // it is a name with auxillaries, which is almost always a
21625 				// family name
21626 			    this.familyName = parts[0];
21627 		    } else {
21628 			    this.givenName = parts[0];
21629 		    }
21630 	    } else if (parts.length === 2) {
21631 		    // we do G F
21632 		    if (this.info.order === 'fgm') {
21633 			    this.givenName = parts[1];
21634 			    this.familyName = parts[0];
21635 		    } else if (this.info.order === "gmf") {
21636 			    this.givenName = parts[0];
21637 			    this.familyName = parts[1];
21638 		    } else if ( typeof (this.info.order) === 'undefined') {
21639 			    if (this._isPatronymicName(parts[1]) === true) {
21640 				    this.middleName = parts[1];
21641 				    this.givenName = parts[0];
21642 			    } else if ((familyIndex = this._findFamilyName(parts)) !== -1) {
21643 				    if (familyIndex === 1) {
21644 					    this.givenName = parts[0];
21645 					    this.familyName = parts[1];
21646 				    } else {
21647 					    this.familyName = parts[0];
21648 					    this.givenName = parts[1];
21649 				    }
21650 
21651 			    } else {
21652 				    this.givenName = parts[0];
21653 				    this.familyName = parts[1];
21654 			    }
21655 
21656 		    }
21657 	    } else if (parts.length >= 3) {
21658 		    // find the first instance of 'and' in the name
21659 		    conjunctionIndex = this._findLastConjunction(parts);
21660 		    var patronymicNameIndex = this._findPatronymicName(parts);
21661 		    if (conjunctionIndex > 0) {
21662 			    // if there's a conjunction that's not the first token, put
21663 				// everything up to and
21664 			    // including the token after it into the first name, the last
21665 				// token into
21666 			    // the family name (if it exists) and everything else in to the
21667 				// middle name
21668 			    // 0 1 2 3 4 5
21669 			    // G A G M M F
21670 			    // G G A G M F
21671 			    // G G G A G F
21672 			    // G G G G A G
21673 			    // if(this.order == "gmf") {
21674 			    this.givenName = parts.slice(0, conjunctionIndex + 2);
21675 
21676 			    if (conjunctionIndex + 1 < parts.length - 1) {
21677 				    this.familyName = parts.splice(parts.length - 1, 1);
21678 				    // //console.log(this.familyName);
21679 				    if (conjunctionIndex + 2 < parts.length - 1) {
21680 					    this.middleName = parts.slice(conjunctionIndex + 2,
21681 					        parts.length - conjunctionIndex - 3);
21682 				    }
21683 			    } else if (this.order == "fgm") {
21684 				    this.familyName = parts.slice(0, conjunctionIndex + 2);
21685 				    if (conjunctionIndex + 1 < parts.length - 1) {
21686 					    this.middleName = parts.splice(parts.length - 1, 1);
21687 					    if (conjunctionIndex + 2 < parts.length - 1) {
21688 						    this.givenName = parts.slice(conjunctionIndex + 2,
21689 						        parts.length - conjunctionIndex - 3);
21690 					    }
21691 				    }
21692 			    }
21693 		    } else if (patronymicNameIndex !== -1) {
21694 			    this.middleName = parts[patronymicNameIndex];
21695 
21696 			    if (patronymicNameIndex === (parts.length - 1)) {
21697 				    this.familyName = parts[0];
21698 				    this.givenName = parts.slice(1, patronymicNameIndex);
21699 			    } else {
21700 				    this.givenName = parts.slice(0, patronymicNameIndex);
21701 
21702 				    this.familyName = parts[parts.length - 1];
21703 			    }
21704 		    } else {
21705 			    this.givenName = parts[0];
21706 
21707 			    this.middleName = parts.slice(1, parts.length - 1);
21708 
21709 			    this.familyName = parts[parts.length - 1];
21710 		    }
21711 	    }
21712     },
21713     
21714     
21715     /**
21716      * @protected
21717      */
21718     _parseWesternName: function (parts) {
21719 
21720         if (this.locale.getLanguage() === "es" || this.locale.getLanguage() === "pt") {
21721             // in spain and mexico and portugal, we parse names differently than in the rest of the world 
21722             // because of the double family names
21723             this._parseSpanishName(parts);
21724         } else if (this.locale.getLanguage() === "ru") {
21725             /*
21726              * In Russian, names can be given equally validly as given-family
21727              * or family-given. Use the value of the "order" property of the
21728              * constructor options to give the default when the order is ambiguous.
21729              */
21730             this._parseRussianName(parts);
21731         } else if (this.locale.getLanguage() === "id") {
21732             // in indonesia, we parse names differently than in the rest of the world 
21733             // because names don't have family names usually.
21734             this._parseIndonesianName(parts);
21735         } else {
21736         	this._parseGenericWesternName(parts);
21737         }
21738     },
21739 
21740     /**
21741      * When sorting names with auxiliary words (like "van der" or "de los"), determine
21742      * which is the "head word" and return a string that can be easily sorted by head
21743      * word. In English, names are always sorted by initial characters. In places like
21744      * the Netherlands or Germany, family names are sorted by the head word of a list
21745      * of names rather than the first element of that name.
21746      * @return {string|undefined} a string containing the family name[s] to be used for sorting
21747      * in the current locale, or undefined if there is no family name in this object
21748      */
21749     getSortFamilyName: function () {
21750         var name,
21751             auxillaries,
21752             auxString,
21753             parts,
21754             i;
21755 
21756         // no name to sort by
21757         if (!this.familyName) {
21758             return undefined;
21759         }
21760 
21761         // first break the name into parts
21762         if (this.info) {
21763             if (this.info.sortByHeadWord) {
21764                 if (typeof (this.familyName) === 'string') {
21765                     name = this.familyName.replace(/\s+/g, ' '); // compress multiple whitespaces
21766                     parts = name.trim().split(' ');
21767                 } else {
21768                     // already split
21769                     parts = this.familyName;
21770                 }
21771 
21772                 auxillaries = this._findPrefix(parts, this.info.auxillaries, false);
21773                 if (auxillaries && auxillaries.length > 0) {
21774                     if (typeof (this.familyName) === 'string') {
21775                         auxString = auxillaries.join(' ');
21776                         name = this.familyName.substring(auxString.length + 1) + ', ' + auxString;
21777                     } else {
21778                         name = parts.slice(auxillaries.length).join(' ') +
21779                             ', ' +
21780                             parts.slice(0, auxillaries.length).join(' ');
21781                     }
21782                 }
21783             } else if (this.info.knownFamilyNames && this.familyName) {
21784                 parts = this.familyName.split('');
21785                 var familyNameArray = this._findPrefix(parts, this.info.knownFamilyNames, true, this.info.noCompoundFamilyNames);
21786                 name = "";
21787                 for (i = 0; i < familyNameArray.length; i++) {
21788                     name += (this.info.knownFamilyNames[familyNameArray[i]] || "");
21789                 }
21790             }
21791         }
21792 
21793         return name || this.familyName;
21794     },
21795 
21796     getHeadFamilyName: function () {},
21797 
21798     /** 
21799      * @protected
21800      * Return a shallow copy of the current instance.
21801      */
21802     clone: function () {
21803         return new Name(this);
21804     }
21805 };
21806 
21807 
21808 /*< NameFmt.js */
21809 /*
21810  * NameFmt.js - Format person names for display
21811  * 
21812  * Copyright © 2013-2015, JEDLSoft
21813  *
21814  * Licensed under the Apache License, Version 2.0 (the "License");
21815  * you may not use this file except in compliance with the License.
21816  * You may obtain a copy of the License at
21817  *
21818  *     http://www.apache.org/licenses/LICENSE-2.0
21819  *
21820  * Unless required by applicable law or agreed to in writing, software
21821  * distributed under the License is distributed on an "AS IS" BASIS,
21822  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21823  *
21824  * See the License for the specific language governing permissions and
21825  * limitations under the License.
21826  */
21827 
21828 /* !depends 
21829 ilib.js
21830 Locale.js
21831 IString.js
21832 Name.js
21833 isPunct.js
21834 Utils.js
21835 */
21836 
21837 // !data name
21838 
21839 
21840 
21841 
21842 /**
21843  * @class
21844  * Creates a formatter that can format person name instances (Name) for display to
21845  * a user. The options may contain the following properties:
21846  * 
21847  * <ul>
21848  * <li><i>locale</i> - Use the conventions of the given locale to construct the name format. 
21849  * <li><i>style</i> - Format the name with the given style. The value of this property
21850  * should be one of the following strings: 
21851  *   <ul>
21852  *     <li><i>short</i> - Format a short name with just the given and family names. eg. "John Smith"
21853  *     <li><i>medium</i> - Format a medium-length name with the given, middle, and family names. 
21854  *     eg. "John James Smith"
21855  *     <li><i>long</i> - Format a long name with all names available in the given name object, including
21856  *     prefixes. eg. "Mr. John James Smith"
21857  *     <li><i>full</i> - Format a long name with all names available in the given name object, including
21858  *     prefixes and suffixes. eg. "Mr. John James Smith, Jr."
21859  *     <li><i>formal_short</i> - Format a name with the honorific or prefix/suffix and the family 
21860  *     name. eg. "Mr. Smith"
21861  *     <li><i>formal_long</i> - Format a name with the honorific or prefix/suffix and the 
21862  *     given and family name. eg. "Mr. John Smith"
21863  *   </ul>
21864  * <li><i>components</i> - Format the name with the given components in the correct
21865  * order for those components. Components are encoded as a string of letters representing
21866  * the desired components:
21867  *   <ul>
21868  *     <li><i>p</i> - prefixes
21869  *     <li><i>g</i> - given name
21870  *     <li><i>m</i> - middle names
21871  *     <li><i>f</i> - family name
21872  *     <li><i>s</i> - suffixes
21873  *     <li><i>h</i> - honorifics (selects the prefix or suffix as required by the locale)
21874  *   </ul>
21875  * <p>
21876  * 
21877  * For example, the string "pf" would mean to only format any prefixes and family names 
21878  * together and leave out all the other parts of the name.<p>
21879  * 
21880  * The components can be listed in any order in the string. The <i>components</i> option 
21881  * overrides the <i>style</i> option if both are specified.
21882  *
21883  * <li>onLoad - a callback function to call when the locale info object is fully 
21884  * loaded. When the onLoad option is given, the localeinfo object will attempt to
21885  * load any missing locale data using the ilib loader callback.
21886  * When the constructor is done (even if the data is already preassembled), the 
21887  * onLoad function is called with the current instance as a parameter, so this
21888  * callback can be used with preassembled or dynamic loading or a mix of the two.
21889  * 
21890  * <li>sync - tell whether to load any missing locale data synchronously or 
21891  * asynchronously. If this option is given as "false", then the "onLoad"
21892  * callback must be given, as the instance returned from this constructor will
21893  * not be usable for a while. 
21894  *
21895  * <li><i>loadParams</i> - an object containing parameters to pass to the 
21896  * loader callback function when locale data is missing. The parameters are not
21897  * interpretted or modified in any way. They are simply passed along. The object 
21898  * may contain any property/value pairs as long as the calling code is in
21899  * agreement with the loader callback function as to what those parameters mean.
21900  * </ul>
21901  * 
21902  * Formatting names is a locale-dependent function, as the order of the components 
21903  * depends on the locale. The following explains some of the details:<p>
21904  * 
21905  * <ul>
21906  * <li>In Western countries, the given name comes first, followed by a space, followed 
21907  * by the family name. In Asian countries, the family name comes first, followed immediately
21908  * by the given name with no space. But, that format is only used with Asian names written
21909  * in ideographic characters. In Asian countries, especially ones where both an Asian and 
21910  * a Western language are used (Hong Kong, Singapore, etc.), the convention is often to 
21911  * follow the language of the name. That is, Asian names are written in Asian style, and 
21912  * Western names are written in Western style. This class follows that convention as
21913  * well. 
21914  * <li>In other Asian countries, Asian names
21915  * written in Latin script are written with Asian ordering. eg. "Xu Ping-an" instead
21916  * of the more Western order "Ping-an Xu", as the order is thought to go with the style
21917  * that is appropriate for the name rather than the style for the language being written.
21918  * <li>In some Spanish speaking countries, people often take both their maternal and
21919  * paternal last names as their own family name. When formatting a short or medium style
21920  * of that family name, only the paternal name is used. In the long style, all the names
21921  * are used. eg. "Juan Julio Raul Lopez Ortiz" took the name "Lopez" from his father and 
21922  * the name "Ortiz" from his mother. His family name would be "Lopez Ortiz". The formatted
21923  * short style of his name would be simply "Juan Lopez" which only uses his paternal
21924  * family name of "Lopez".
21925  * <li>In many Western languages, it is common to use auxillary words in family names. For
21926  * example, the family name of "Ludwig von Beethoven" in German is "von Beethoven", not 
21927  * "Beethoven". This class ensures that the family name is formatted correctly with 
21928  * all auxillary words.   
21929  * </ul>
21930  * 
21931  * 
21932  * @constructor
21933  * @param {Object} options A set of options that govern how the formatter will behave
21934  */
21935 var NameFmt = function(options) {
21936 	var sync = true;
21937 	
21938 	this.style = "short";
21939 	this.loadParams = {};
21940 	
21941 	if (options) {
21942 		if (options.locale) {
21943 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
21944 		}
21945 		
21946 		if (options.style) {
21947 			this.style = options.style;
21948 		}
21949 		
21950 		if (options.components) {
21951 			this.components = options.components;
21952 		}
21953 		
21954 		if (typeof(options.sync) !== 'undefined') {
21955 			sync = !!options.sync;
21956 		}
21957 		
21958 		if (typeof(options.loadParams) !== 'undefined') {
21959 			this.loadParams = options.loadParams;
21960 		}
21961 	}
21962 	
21963 	// set up defaults in case we need them
21964 	this.defaultEuroTemplate = new IString("{prefix} {givenName} {middleName} {familyName}{suffix}");
21965 	this.defaultAsianTemplate = new IString("{prefix}{familyName}{givenName}{middleName}{suffix}");
21966 	this.useFirstFamilyName = false;
21967 
21968 	switch (this.style) {
21969 		default:
21970 		case "s":
21971 		case "short":
21972 			this.style = "short";
21973 			break;
21974 		case "m":
21975 		case "medium":
21976 			this.style = "medium";
21977 			break;
21978 		case "l":
21979 		case "long":
21980 			this.style = "long";
21981 			break;
21982 		case "f":
21983 		case "full":
21984 			this.style = "full";
21985 			break;
21986 		case "fs":
21987 		case "formal_short":
21988 			this.style = "formal_short";
21989 			break;
21990 		case "fl":
21991 		case "formal_long":
21992 			this.style = "formal_long";
21993 			break;
21994 	}
21995 
21996 	this.locale = this.locale || new Locale();
21997 	
21998 	isPunct._init(sync, this.loadParams, ilib.bind(this, function() {
21999 		Utils.loadData({
22000 			object: "Name", 
22001 			locale: this.locale, 
22002 			name: "name.json", 
22003 			sync: sync, 
22004 			loadParams: this.loadParams, 
22005 			callback: ilib.bind(this, function (info) {
22006 				if (!info) {
22007 					info = Name.defaultInfo;
22008 					var spec = this.locale.getSpec().replace(/-/g, "_");
22009 					ilib.data.cache.Name[spec] = info;
22010 				}
22011 				this.info = info;
22012 				this._init();
22013 				if (options && typeof(options.onLoad) === 'function') {
22014 					options.onLoad(this);
22015 				}
22016 			})
22017 		});
22018 	}));
22019 };
22020 
22021 NameFmt.prototype = {
22022 	/**                          
22023 	 * @protected
22024 	 */
22025 	_init: function() {
22026 		var arr;
22027 		this.comps = {};
22028 		
22029 		if (this.components) {
22030 			var valids = {"p":1,"g":1,"m":1,"f":1,"s":1,"h":1};
22031 			arr = this.components.split("");
22032 			this.comps = {};
22033 			for (var i = 0; i < arr.length; i++) {
22034 				if (valids[arr[i].toLowerCase()]) {
22035 					this.comps[arr[i].toLowerCase()] = true;
22036 				}
22037 			}
22038 		} else {
22039 			var comps = this.info.components[this.style];
22040 			if (typeof(comps) === "string") {
22041 				comps.split("").forEach(ilib.bind(this, function(c) {
22042 					this.comps[c] = true;
22043 				}));
22044 			} else {
22045 				this.comps = comps;
22046 			}
22047 		}
22048 
22049 		this.template = new IString(this.info.format);
22050 		
22051 		if (this.locale.language === "es" && (this.style !== "long" && this.style !== "full")) {
22052 			this.useFirstFamilyName = true;	// in spanish, they have 2 family names, the maternal and paternal
22053 		}
22054 
22055 		this.isAsianLocale = (this.info.nameStyle === "asian");
22056 	},
22057 
22058 	/**
22059 	 * adjoin auxillary words to their head words
22060 	 * @protected
22061 	 */
22062 	_adjoinAuxillaries: function (parts, namePrefix) {
22063 		var start, i, prefixArray, prefix, prefixLower;
22064 		
22065 		//console.info("_adjoinAuxillaries: finding and adjoining aux words in " + parts.join(' '));
22066 		
22067 		if ( this.info.auxillaries && (parts.length > 2 || namePrefix) ) {
22068 			for ( start = 0; start < parts.length-1; start++ ) {
22069 				for ( i = parts.length; i > start; i-- ) {
22070 					prefixArray = parts.slice(start, i);
22071 					prefix = prefixArray.join(' ');
22072 					prefixLower = prefix.toLowerCase();
22073 					prefixLower = prefixLower.replace(/[,\.]/g, '');  // ignore commas and periods
22074 					
22075 					//console.info("_adjoinAuxillaries: checking aux prefix: '" + prefixLower + "' which is " + start + " to " + i);
22076 					
22077 					if ( prefixLower in this.info.auxillaries ) {
22078 						//console.info("Found! Old parts list is " + JSON.stringify(parts));
22079 						parts.splice(start, i+1-start, prefixArray.concat(parts[i]));
22080 						//console.info("_adjoinAuxillaries: Found! New parts list is " + JSON.stringify(parts));
22081 						i = start;
22082 					}
22083 				}
22084 			}
22085 		}
22086 		
22087 		//console.info("_adjoinAuxillaries: done. Result is " + JSON.stringify(parts));
22088 
22089 		return parts;
22090 	},
22091 
22092 	/**
22093 	 * Return the locale for this formatter instance.
22094 	 * @return {Locale} the locale instance for this formatter
22095 	 */
22096 	getLocale: function () {
22097 		return this.locale;
22098 	},
22099 	
22100 	/**
22101 	 * Return the style of names returned by this formatter
22102 	 * @return {string} the style of names returned by this formatter
22103 	 */
22104 	getStyle: function () {
22105 		return this.style;
22106 	},
22107 	
22108 	/**
22109 	 * Return the list of components used to format names in this formatter
22110 	 * @return {string} the list of components
22111 	 */
22112 	getComponents: function () {
22113 		return this.components;
22114 	},
22115 	
22116 	/**
22117 	 * Format the name for display in the current locale with the options set up
22118 	 * in the constructor of this formatter instance.<p>
22119 	 * 
22120 	 * If the name does not contain all the parts required for the style, those parts
22121 	 * will be left blank.<p>
22122 	 * 
22123 	 * There are two basic styles of formatting: European, and Asian. If this formatter object
22124 	 * is set for European style, but an Asian name is passed to the format method, then this
22125 	 * method will format the Asian name with a generic Asian template. Similarly, if the
22126 	 * formatter is set for an Asian style, and a European name is passed to the format method,
22127 	 * the formatter will use a generic European template.<p>
22128 	 * 
22129 	 * This means it is always safe to format any name with a formatter for any locale. You should
22130 	 * always get something at least reasonable as output.<p>
22131 	 * 
22132 	 * @param {Name|Object} name the name instance to format, or an object containing name parts to format
22133 	 * @return {string|undefined} the name formatted according to the style of this formatter instance
22134 	 */
22135 	format: function(name) {
22136 		var formatted, temp, modified, isAsianName;
22137 		var currentLanguage = this.locale.getLanguage();
22138 		 
22139 		if (!name || typeof(name) !== 'object') {
22140 			return undefined;
22141 		}
22142 		if (!(name instanceof Name)) {
22143 			// if the object is not a name, implicitly convert to a name so that the code below works
22144 			name = new Name(name, {locale: this.locale});
22145 		}
22146 		
22147 		if ((typeof(name.isAsianName) === 'boolean' && !name.isAsianName) ||
22148 				Name._isEuroName([name.givenName, name.middleName, name.familyName].join(""), currentLanguage)) {
22149 			isAsianName = false;	// this is a euro name, even if the locale is asian
22150 			modified = name.clone();
22151 			
22152 			// handle the case where there is no space if there is punctuation in the suffix like ", Phd". 
22153 			// Otherwise, put a space in to transform "PhD" to " PhD"
22154 			/*
22155 			console.log("suffix is " + modified.suffix);
22156 			if ( modified.suffix ) {
22157 				console.log("first char is " + modified.suffix.charAt(0));
22158 				console.log("isPunct(modified.suffix.charAt(0)) is " + isPunct(modified.suffix.charAt(0)));
22159 			}
22160 			*/
22161 			if (modified.suffix && isPunct(modified.suffix.charAt(0)) === false) {
22162 				modified.suffix = ' ' + modified.suffix; 
22163 			}
22164 			
22165 			if (this.useFirstFamilyName && name.familyName) {
22166 				var familyNameParts = modified.familyName.trim().split(' ');
22167 				if (familyNameParts.length > 1) {
22168 					familyNameParts = this._adjoinAuxillaries(familyNameParts, name.prefix);
22169 				}	//in spain and mexico, we parse names differently than in the rest of the world
22170 	
22171 				modified.familyName = familyNameParts[0];
22172 			}
22173 		
22174 			modified._joinNameArrays();
22175 		} else {
22176 			isAsianName = true;
22177 			modified = name;
22178 		}
22179 		
22180 		if (!this.template || isAsianName !== this.isAsianLocale) {
22181 			temp = isAsianName ? this.defaultAsianTemplate : this.defaultEuroTemplate;
22182 		} else {
22183 			temp = this.template;
22184 		}
22185 		
22186 		// use the honorific as the prefix or the suffix as appropriate for the order of the name
22187 		if (modified.honorific) {
22188 			if ((this.order === 'fg' || isAsianName) && currentLanguage !== "ko") {
22189 				if (!modified.suffix) {
22190 					modified.suffix = modified.honorific
22191 				}
22192 			} else {
22193 				if (!modified.prefix) {
22194 					modified.prefix = modified.honorific
22195 				}
22196 			}
22197 		}
22198 
22199 		var parts = {
22200 			prefix: this.comps["p"] && modified.prefix || "",
22201 			givenName: this.comps["g"] && modified.givenName || "",
22202 			middleName: this.comps["m"] && modified.middleName || "",
22203 			familyName: this.comps["f"] && modified.familyName || "",
22204 			suffix: this.comps["s"] && modified.suffix || ""
22205 		};
22206 		
22207 		formatted = temp.format(parts);
22208 		return formatted.replace(/\s+/g, ' ').trim();
22209 	}
22210 };
22211 
22212 
22213 
22214 /*< Address.js */
22215 /*
22216  * Address.js - Represent a mailing address
22217  * 
22218  * Copyright © 2013-2015, JEDLSoft
22219  *
22220  * Licensed under the Apache License, Version 2.0 (the "License");
22221  * you may not use this file except in compliance with the License.
22222  * You may obtain a copy of the License at
22223  *
22224  *     http://www.apache.org/licenses/LICENSE-2.0
22225  *
22226  * Unless required by applicable law or agreed to in writing, software
22227  * distributed under the License is distributed on an "AS IS" BASIS,
22228  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22229  *
22230  * See the License for the specific language governing permissions and
22231  * limitations under the License.
22232  */
22233 
22234 /*globals console RegExp */
22235 
22236 /* !depends 
22237 ilib.js
22238 Utils.js
22239 JSUtils.js
22240 Locale.js 
22241 isIdeo.js 
22242 isAscii.js
22243 isDigit.js
22244 IString.js
22245 */
22246 
22247 // !data address countries nativecountries ctrynames
22248 
22249 
22250 /**
22251  * @class
22252  * Create a new Address instance and parse a physical address.<p>
22253  * 
22254  * This function parses a physical address written in a free-form string. 
22255  * It returns an object with a number of properties from the list below 
22256  * that it may have extracted from that address.<p>
22257  * 
22258  * The following is a list of properties that the algorithm will return:<p>
22259  * 
22260  * <ul>
22261  * <li><i>streetAddress</i>: The street address, including house numbers and all.
22262  * <li><i>locality</i>: The locality of this address (usually a city or town). 
22263  * <li><i>region</i>: The region where the locality is located. In the US, this
22264  * corresponds to states. In other countries, this may be provinces,
22265  * cantons, prefectures, etc. In some smaller countries, there are no
22266  * such divisions.
22267  * <li><i>postalCode</i>: Country-specific code for expediting mail. In the US, 
22268  * this is the zip code.
22269  * <li><i>country</i>: The country of the address.
22270  * <li><i>countryCode</i>: The ISO 3166 2-letter region code for the destination
22271  * country in this address.
22272  * </ul> 
22273  * 
22274  * The above properties will not necessarily appear in the instance. For 
22275  * any individual property, if the free-form address does not contain 
22276  * that property or it cannot be parsed out, the it is left out.<p>
22277  * 
22278  * The options parameter may contain any of the following properties:
22279  * 
22280  * <ul>
22281  * <li><i>locale</i> - locale or localeSpec to use to parse the address. If not 
22282  * specified, this function will use the current ilib locale
22283  * 
22284  * <li><i>onLoad</i> - a callback function to call when the address info for the
22285  * locale is fully loaded and the address has been parsed. When the onLoad 
22286  * option is given, the address object 
22287  * will attempt to load any missing locale data using the ilib loader callback.
22288  * When the constructor is done (even if the data is already preassembled), the 
22289  * onLoad function is called with the current instance as a parameter, so this
22290  * callback can be used with preassembled or dynamic loading or a mix of the two. 
22291  * 
22292  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
22293  * asynchronously. If this option is given as "false", then the "onLoad"
22294  * callback must be given, as the instance returned from this constructor will
22295  * not be usable for a while. 
22296  *
22297  * <li><i>loadParams</i> - an object containing parameters to pass to the 
22298  * loader callback function when locale data is missing. The parameters are not
22299  * interpretted or modified in any way. They are simply passed along. The object 
22300  * may contain any property/value pairs as long as the calling code is in
22301  * agreement with the loader callback function as to what those parameters mean.
22302  * </ul>
22303  * 
22304  * When an address cannot be parsed properly, the entire address will be placed
22305  * into the streetAddress property.<p>
22306  * 
22307  * When the freeformAddress is another Address, this will act like a copy
22308  * constructor.<p>
22309  * 
22310  * 
22311  * @constructor
22312  * @param {string|Address} freeformAddress free-form address to parse, or a
22313  * javascript object containing the fields
22314  * @param {Object} options options to the parser
22315  */
22316 var Address = function (freeformAddress, options) {
22317 	var address;
22318 
22319 	if (!freeformAddress) {
22320 		return undefined;
22321 	}
22322 
22323 	this.sync = true;
22324 	this.loadParams = {};
22325 	
22326 	if (options) {
22327 		if (options.locale) {
22328 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
22329 		}
22330 		
22331 		if (typeof(options.sync) !== 'undefined') {
22332 			this.sync = !!options.sync;
22333 		}
22334 		
22335 		if (options.loadParams) {
22336 			this.loadParams = options.loadParams;
22337 		}
22338 	}
22339 
22340 	this.locale = this.locale || new Locale();
22341 	// initialize from an already parsed object
22342 	if (typeof(freeformAddress) === 'object') {
22343 		/**
22344 		 * The street address, including house numbers and all.
22345 		 * @type {string|undefined} 
22346 		 */
22347 		this.streetAddress = freeformAddress.streetAddress;
22348 		/**
22349 		 * The locality of this address (usually a city or town).
22350 		 * @type {string|undefined} 
22351 		 */
22352 		this.locality = freeformAddress.locality;
22353 		/**
22354 		 * The region (province, canton, prefecture, state, etc.) where the address is located.
22355 		 * @type {string|undefined} 
22356 		 */
22357 		this.region = freeformAddress.region;
22358 		/**
22359 		 * Country-specific code for expediting mail. In the US, this is the zip code.
22360 		 * @type {string|undefined} 
22361 		 */
22362 		this.postalCode = freeformAddress.postalCode;
22363 		/**
22364 		 * Optional city-specific code for a particular post office, used to expidite
22365 		 * delivery.
22366 		 * @type {string|undefined} 
22367 		 */
22368 		this.postOffice = freeformAddress.postOffice;
22369 		/**
22370 		 * The country of the address.
22371 		 * @type {string|undefined}
22372 		 */
22373 		this.country = freeformAddress.country;
22374 		if (freeformAddress.countryCode) {
22375 			/**
22376 			 * The 2 or 3 letter ISO 3166 region code for the destination country in this address.
22377 			 * @type {string} 
22378 			 */
22379 			this.countryCode = freeformAddress.countryCode;
22380 		}
22381 		if (freeformAddress.format) {
22382 			/**
22383 			 * private
22384 			 * @type {string}
22385 			 */
22386 			this.format = freeformAddress.format;
22387 		}
22388 		return this;
22389 	}
22390 
22391 	address = freeformAddress.replace(/[ \t\r]+/g, " ").trim();
22392 	address = address.replace(/[\s\n]+$/, "");
22393 	address = address.replace(/^[\s\n]+/, "");
22394 	//console.log("\n\n-------------\nAddress is '" + address + "'");
22395 	
22396 	this.lines = address.split(/[,,\n]/g);
22397 	this.removeEmptyLines(this.lines);
22398 	
22399 	isAscii._init(this.sync, this.loadParams, ilib.bind(this, function() {
22400 		isIdeo._init(this.sync, this.loadParams, ilib.bind(this, function() {
22401 			isDigit._init(this.sync, this.loadParams, ilib.bind(this, function() {
22402 				if (typeof(ilib.data.nativecountries) === 'undefined') {
22403 					Utils.loadData({
22404 						object: "Address",
22405 						name: "nativecountries.json", // countries in their own language 
22406 						locale: "-", // only need to load the root file 
22407 						nonlocale: true,
22408 						sync: this.sync, 
22409 						loadParams: this.loadParams, 
22410 						callback: ilib.bind(this, function(nativecountries) {
22411 							ilib.data.nativecountries = nativecountries;
22412 							this._loadCountries(options && options.onLoad);
22413 						})
22414 					});
22415 				} else {
22416 					this._loadCountries(options && options.onLoad);
22417 				}
22418 			}));
22419 		}));
22420 	}));
22421 };
22422 
22423 /** @protected */
22424 Address.prototype = {
22425 	/**
22426 	 * @private
22427 	 */
22428 	_loadCountries: function(onLoad) {
22429 		if (typeof(ilib.data.countries) === 'undefined') {
22430 			Utils.loadData({
22431 				object: "Address",
22432 				name: "countries.json", // countries in English
22433 				locale: "-", // only need to load the root file
22434 				nonlocale: true,
22435 				sync: this.sync, 
22436 				loadParams: this.loadParams, 
22437 				callback: ilib.bind(this, function(countries) {
22438 					ilib.data.countries = countries;
22439 					this._loadCtrynames(onLoad);
22440 				})
22441 			});
22442 		} else {
22443 			this._loadCtrynames(onLoad);
22444 		}
22445 	},
22446 
22447 	/**
22448 	 * @private
22449 	 */
22450 	_loadCtrynames: function(onLoad) {
22451 		Utils.loadData({
22452 			name: "ctrynames.json", 
22453 			object: "Address", 
22454 			locale: this.locale,
22455 			sync: this.sync, 
22456 			loadParams: JSUtils.merge(this.loadParams, {returnOne: true}),
22457 			callback: ilib.bind(this, function(ctrynames) {
22458 			    this.ctrynames = ctrynames;
22459 				this._determineDest(ctrynames, onLoad);
22460 			})
22461 		});
22462 	},
22463 	
22464 	/**
22465 	 * @private
22466 	 * @param {Object?} ctrynames
22467 	 */
22468 	_findDest: function (ctrynames) {
22469 		var match;
22470 		
22471 		for (var countryName in ctrynames) {
22472 			if (countryName && countryName !== "generated") {
22473 				// find the longest match in the current table
22474 				// ctrynames contains the country names mapped to region code
22475 				// for efficiency, only test for things longer than the current match
22476 				if (!match || match.text.length < countryName.length) {
22477 					var temp = this._findCountry(countryName);
22478 					if (temp) {
22479 						match = temp;
22480 						this.country = match.text;
22481 						this.countryCode = ctrynames[countryName];
22482 					}
22483 				}
22484 			}
22485 		}
22486 		return match;
22487 	},
22488 	
22489 	/**
22490 	 * @private
22491 	 * @param {Object?} localizedCountries
22492 	 * @param {function(Address):undefined} callback
22493 	 */
22494 	_determineDest: function (localizedCountries, callback) {
22495 		var match;
22496 		
22497 		/*
22498 		 * First, find the name of the destination country, as that determines how to parse
22499 		 * the rest of the address. For any address, there are three possible ways 
22500 		 * that the name of the country could be written:
22501 		 * 1. In the current language
22502 		 * 2. In its own native language
22503 		 * 3. In English
22504 		 * We'll try all three.
22505 		 */
22506 		var tables = [];
22507 		if (localizedCountries) {
22508 			tables.push(localizedCountries);
22509 		}
22510 		tables.push(ilib.data.nativecountries);
22511 		tables.push(ilib.data.countries);
22512 		
22513 		for (var i = 0; i < tables.length; i++) {
22514 			match = this._findDest(tables[i]);
22515 			
22516 			if (match) {
22517 				this.lines[match.line] = this.lines[match.line].substring(0, match.start) + this.lines[match.line].substring(match.start + match.text.length);
22518 
22519 				this._init(callback);
22520 				return;
22521 			}
22522 		}
22523 		
22524 		// no country, so try parsing it as if we were in the same country
22525 		this.country = undefined;
22526 		this.countryCode = this.locale.getRegion();
22527 		this._init(callback);
22528 	},
22529 
22530 	/**
22531 	 * @private
22532 	 * @param {function(Address):undefined} callback
22533 	 */
22534 	_init: function(callback) {
22535 		Utils.loadData({
22536 			object: "Address", 
22537 			locale: new Locale(this.countryCode), 
22538 			name: "address.json", 
22539 			sync: this.sync, 
22540 			loadParams: this.loadParams,
22541 			callback: ilib.bind(this, function(info) {
22542 				if (!info || JSUtils.isEmpty(info) || !info.fields) {
22543 					// load the "unknown" locale instead
22544 					Utils.loadData({
22545 						object: "Address", 
22546 						locale: new Locale("XX"), 
22547 						name: "address.json", 
22548 						sync: this.sync, 
22549 						loadParams: this.loadParams,
22550 						callback: ilib.bind(this, function(info) {
22551 							this.info = info;
22552 							this._parseAddress();
22553 							if (typeof(callback) === 'function') {
22554 								callback(this);
22555 							}	
22556 						})
22557 					});
22558 				} else {
22559 					this.info = info;
22560 					this._parseAddress();
22561 					if (typeof(callback) === 'function') {
22562 						callback(this);
22563 					}
22564 				}
22565 			})
22566 		});
22567 	},
22568 
22569 	/**
22570 	 * @private
22571 	 */
22572 	_parseAddress: function() {
22573 		// clean it up first
22574 		var i, 
22575 			asianChars = 0, 
22576 			latinChars = 0,
22577 			startAt,
22578 			infoFields,
22579 			field,
22580 			pattern,
22581 			matchFunction,
22582 			match,
22583 			fieldNumber;
22584 		
22585 		// for locales that support both latin and asian character addresses, 
22586 		// decide if we are parsing an asian or latin script address
22587 		if (this.info && this.info.multiformat) {
22588 			for (var j = 0; j < this.lines.length; j++) {
22589 				var line = new IString(this.lines[j]);
22590 				var it = line.charIterator();
22591 				while (it.hasNext()) {
22592 					var c = it.next();
22593 					if (isIdeo(c) || 
22594 					        CType.withinRange(c, "hangul") || 
22595 					        CType.withinRange(c, 'katakana') ||
22596 					        CType.withinRange(c, 'hiragana') ||
22597 					        CType.withinRange(c, 'bopomofo')) {
22598 						asianChars++;
22599 					} else if (isAscii(c) && !isDigit(c)) {
22600 						latinChars++;
22601 					}
22602 				}
22603 			}
22604 			
22605 			this.format = (asianChars >= latinChars) ? "asian" : "latin";
22606 			startAt = this.info.startAt[this.format];
22607 			infoFields = this.info.fields[this.format];
22608 			// //console.log("multiformat locale: format is now " + this.format);
22609 		} else {
22610 			startAt = (this.info && this.info.startAt) || "end";
22611 			infoFields = (this.info && this.info.fields) || [];
22612 		}
22613 		this.compare = (startAt === "end") ? this.endsWith : this.startsWith;
22614 		
22615 		//console.log("this.lines is: " + JSON.stringify(this.lines));
22616 		
22617 		for (i = 0; i < infoFields.length && this.lines.length > 0; i++) {
22618 			field = infoFields[i];
22619 			this.removeEmptyLines(this.lines);
22620 			//console.log("Searching for field " + field.name);
22621 			if (field.pattern) {
22622 				if (typeof(field.pattern) === 'string') {
22623 					pattern = new RegExp(field.pattern, "img");
22624 					matchFunction = this.matchRegExp;
22625 				} else {
22626 					pattern = field.pattern;
22627 					matchFunction = this.matchPattern;
22628 				}
22629 					
22630 				switch (field.line) {
22631 				case 'startAtFirst':
22632 					for (fieldNumber = 0; fieldNumber < this.lines.length; fieldNumber++) {
22633 						match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
22634 						if (match) {
22635 							break;
22636 						}
22637 					}
22638 					break;
22639 				case 'startAtLast':
22640 					for (fieldNumber = this.lines.length-1; fieldNumber >= 0; fieldNumber--) {
22641 						match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
22642 						if (match) {
22643 							break;
22644 						}
22645 					}
22646 					break;
22647 				case 'first':
22648 					fieldNumber = 0;
22649 					match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
22650 					break;
22651 				case 'last':
22652 				default:
22653 					fieldNumber = this.lines.length - 1;
22654 					match = matchFunction(this, this.lines[fieldNumber], pattern, field.matchGroup, startAt);
22655 					break;
22656 				}
22657 				if (match) {
22658 					// //console.log("found match for " + field.name + ": " + JSON.stringify(match));
22659 					// //console.log("remaining line is " + match.line);
22660 					this.lines[fieldNumber] = match.line;
22661 					this[field.name] = match.match;
22662 				}
22663 			} else {
22664 				// if nothing is given, default to taking the whole field
22665 				this[field.name] = this.lines.splice(fieldNumber,1)[0].trim();
22666 				//console.log("typeof(this[field.name]) is " + typeof(this[field.name]) + " and value is " + JSON.stringify(this[field.name]));
22667 			}
22668 		}
22669 			
22670 		// all the left overs go in the street address field
22671 		this.removeEmptyLines(this.lines);
22672 		if (this.lines.length > 0) {
22673 			//console.log("this.lines is " + JSON.stringify(this.lines) + " and splicing to get streetAddress");
22674 			// Korea uses spaces between words, despite being an "asian" locale
22675 			var joinString = (this.info.joinString && this.info.joinString[this.format]) || ((this.format && this.format === "asian") ? "" : ", ");
22676 			this.streetAddress = this.lines.join(joinString).trim();
22677 		}
22678 		
22679 		this.lines = undefined;
22680 		//console.log("final result is " + JSON.stringify(this));
22681 	},
22682 	
22683 	/**
22684 	 * @protected
22685 	 * Find the named country either at the end or the beginning of the address.
22686 	 */
22687 	_findCountry: function(name) {
22688 		var start = -1, match, line = 0;
22689 		
22690 		if (this.lines.length > 0) {
22691 			start = this.startsWith(this.lines[line], name);
22692 			if (start === -1) {
22693 				line = this.lines.length-1;
22694 				start = this.endsWith(this.lines[line], name);
22695 			}
22696 			if (start !== -1) {
22697 				match = {
22698 					text: this.lines[line].substring(start, start + name.length),
22699 					line: line,
22700 					start: start
22701 				};
22702 			}
22703 		}
22704 		
22705 		return match;
22706 	},
22707 	
22708 	endsWith: function (subject, query) {
22709 		var start = subject.length-query.length,
22710 			i,
22711 			pat;
22712 		//console.log("endsWith: checking " + query + " against " + subject);
22713 		for (i = 0; i < query.length; i++) {
22714 			// TODO: use case mapper instead of toLowerCase()
22715 			if (subject.charAt(start+i).toLowerCase() !== query.charAt(i).toLowerCase()) {
22716 				return -1;
22717 			}
22718 		}
22719 		if (start > 0) {
22720 			pat = /\s/;
22721 			if (!pat.test(subject.charAt(start-1))) {
22722 				// make sure if we are not at the beginning of the string, that the match is 
22723 				// not the end of some other word
22724 				return -1;
22725 			}
22726 		}
22727 		return start;
22728 	},
22729 	
22730 	startsWith: function (subject, query) {
22731 		var i;
22732 		// //console.log("startsWith: checking " + query + " against " + subject);
22733 		for (i = 0; i < query.length; i++) {
22734 			// TODO: use case mapper instead of toLowerCase()
22735 			if (subject.charAt(i).toLowerCase() !== query.charAt(i).toLowerCase()) {
22736 				return -1;
22737 			}
22738 		}
22739 		return 0;
22740 	},
22741 	
22742 	removeEmptyLines: function (arr) {
22743 		var i = 0;
22744 		
22745 		while (i < arr.length) {
22746 			if (arr[i]) {
22747 				arr[i] = arr[i].trim();
22748 				if (arr[i].length === 0) {
22749 					arr.splice(i,1);
22750 				} else {
22751 					i++;
22752 				}
22753 			} else {
22754 				arr.splice(i,1);
22755 			}
22756 		}
22757 	},
22758 	
22759 	matchRegExp: function(address, line, expression, matchGroup, startAt) {
22760 		var lastMatch,
22761 			match,
22762 			ret = {},
22763 			last;
22764 		
22765 		//console.log("searching for regexp " + expression.source + " in line " + line);
22766 		
22767 		match = expression.exec(line);
22768 		if (startAt === 'end') {
22769 			while (match !== null && match.length > 0) {
22770 				//console.log("found matches " + JSON.stringify(match));
22771 				lastMatch = match;
22772 				match = expression.exec(line);
22773 			}
22774 			match = lastMatch;
22775 		}
22776 		
22777 		if (match && match !== null) {
22778 			//console.log("found matches " + JSON.stringify(match));
22779 			matchGroup = matchGroup || 0;
22780 			if (match[matchGroup] !== undefined) {
22781 				ret.match = match[matchGroup].trim();
22782 				ret.match = ret.match.replace(/^\-|\-+$/, '');
22783 				ret.match = ret.match.replace(/\s+$/, '');
22784 				last = (startAt === 'end') ? line.lastIndexOf(match[matchGroup]) : line.indexOf(match[matchGroup]); 
22785 				//console.log("last is " + last);
22786 				ret.line = line.slice(0,last);
22787 				if (address.format !== "asian") {
22788 					ret.line += " ";
22789 				}
22790 				ret.line += line.slice(last+match[matchGroup].length);
22791 				ret.line = ret.line.trim();
22792 				//console.log("found match " + ret.match + " from matchgroup " + matchGroup + " and rest of line is " + ret.line);
22793 				return ret;
22794 			}
22795 		//} else {
22796 			//console.log("no match");
22797 		}
22798 		
22799 		return undefined;
22800 	},
22801 	
22802 	matchPattern: function(address, line, pattern, matchGroup) {
22803 		var start,
22804 			j,
22805 			ret = {};
22806 		
22807 		//console.log("searching in line " + line);
22808 		
22809 		// search an array of possible fixed strings
22810 		//console.log("Using fixed set of strings.");
22811 		for (j = 0; j < pattern.length; j++) {
22812 			start = address.compare(line, pattern[j]); 
22813 			if (start !== -1) {
22814                 ret.match = line.substring(start, start+pattern[j].length);
22815                 if (start !== 0) {
22816                     ret.line = line.substring(0,start).trim();
22817                 } else {
22818                     ret.line = line.substring(pattern[j].length).trim();
22819                 }
22820 				//console.log("found match " + ret.match + " and rest of line is " + ret.line);
22821                 return ret;
22822 			}
22823 		}
22824 		
22825 		return undefined;
22826 	}
22827 };
22828 
22829 
22830 
22831 /*< AddressFmt.js */
22832 /*
22833  * AddressFmt.js - Format an address
22834  * 
22835  * Copyright © 2013-2015, JEDLSoft
22836  *
22837  * Licensed under the Apache License, Version 2.0 (the "License");
22838  * you may not use this file except in compliance with the License.
22839  * You may obtain a copy of the License at
22840  *
22841  *     http://www.apache.org/licenses/LICENSE-2.0
22842  *
22843  * Unless required by applicable law or agreed to in writing, software
22844  * distributed under the License is distributed on an "AS IS" BASIS,
22845  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22846  *
22847  * See the License for the specific language governing permissions and
22848  * limitations under the License.
22849  */
22850 
22851 /* !depends 
22852 ilib.js 
22853 Locale.js
22854 Address.js
22855 IString.js
22856 Utils.js
22857 JSUtils.js
22858 */
22859 
22860 // !data address addressres regionnames
22861 
22862 
22863 
22864 /**
22865  * @class
22866  * Create a new formatter object to format physical addresses in a particular way.
22867  *
22868  * The options object may contain the following properties, both of which are optional:
22869  *
22870  * <ul>
22871  * <li><i>locale</i> - the locale to use to format this address. If not specified, it uses the default locale
22872  * 
22873  * <li><i>style</i> - the style of this address. The default style for each country usually includes all valid 
22874  * fields for that country.
22875  * 
22876  * <li><i>onLoad</i> - a callback function to call when the address info for the
22877  * locale is fully loaded and the address has been parsed. When the onLoad 
22878  * option is given, the address formatter object 
22879  * will attempt to load any missing locale data using the ilib loader callback.
22880  * When the constructor is done (even if the data is already preassembled), the 
22881  * onLoad function is called with the current instance as a parameter, so this
22882  * callback can be used with preassembled or dynamic loading or a mix of the two. 
22883  * 
22884  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
22885  * asynchronously. If this option is given as "false", then the "onLoad"
22886  * callback must be given, as the instance returned from this constructor will
22887  * not be usable for a while. 
22888  *
22889  * <li><i>loadParams</i> - an object containing parameters to pass to the 
22890  * loader callback function when locale data is missing. The parameters are not
22891  * interpretted or modified in any way. They are simply passed along. The object 
22892  * may contain any property/value pairs as long as the calling code is in
22893  * agreement with the loader callback function as to what those parameters mean.
22894  * </ul>
22895  * 
22896  * 
22897  * @constructor
22898  * @param {Object} options options that configure how this formatter should work
22899  * Returns a formatter instance that can format multiple addresses.
22900  */
22901 var AddressFmt = function(options) {
22902 	this.sync = true;
22903 	this.styleName = 'default';
22904 	this.loadParams = {};
22905 	this.locale = new Locale();
22906 	
22907 	if (options) {
22908 		if (options.locale) {
22909 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
22910 		}
22911 		
22912 		if (typeof(options.sync) !== 'undefined') {
22913 			this.sync = !!options.sync;
22914 		}
22915 		
22916 		if (options.style) {
22917 			this.styleName = options.style;
22918 		}
22919 		
22920 		if (options.loadParams) {
22921 			this.loadParams = options.loadParams;
22922 		}
22923 	}
22924 	
22925 	// console.log("Creating formatter for region: " + this.locale.region);
22926 	Utils.loadData({
22927 		name: "address.json",
22928 		object: "AddressFmt", 
22929 		locale: this.locale,
22930 		sync: this.sync, 
22931 		loadParams: this.loadParams, 
22932 		callback: ilib.bind(this, function(info) {
22933 			if (!info || JSUtils.isEmpty(info)) {
22934 				// load the "unknown" locale instead
22935 				Utils.loadData({
22936 					name: "address.json",
22937 					object: "AddressFmt", 
22938 					locale: new Locale("XX"),
22939 					sync: this.sync, 
22940 					loadParams: this.loadParams, 
22941 					callback: ilib.bind(this, function(info) {
22942 						this.info = info;
22943 						this._init();
22944 						if (options && typeof(options.onLoad) === 'function') {
22945 							options.onLoad(this);
22946 						}
22947 					})
22948 				});
22949 			} else {
22950 				this.info = info;
22951 				this._init();
22952 				if (options && typeof(options.onLoad) === 'function') {
22953 					options.onLoad(this);
22954 				}
22955 			}
22956 		})
22957 	});
22958 };
22959 
22960 /**
22961  * @private
22962  */
22963 AddressFmt.prototype._init = function () {
22964 	this.style = this.info && this.info.formats && this.info.formats[this.styleName];
22965 	
22966 	// use generic default -- should not happen, but just in case...
22967 	this.style = this.style || (this.info && this.info.formats && this.info.formats["default"]) || "{streetAddress}\n{locality} {region} {postalCode}\n{country}";
22968 };
22969 
22970 /**
22971  * This function formats a physical address (Address instance) for display. 
22972  * Whitespace is trimmed from the beginning and end of final resulting string, and 
22973  * multiple consecutive whitespace characters in the middle of the string are 
22974  * compressed down to 1 space character.
22975  * 
22976  * If the Address instance is for a locale that is different than the locale for this
22977  * formatter, then a hybrid address is produced. The country name is located in the
22978  * correct spot for the current formatter's locale, but the rest of the fields are
22979  * formatted according to the default style of the locale of the actual address.
22980  * 
22981  * Example: a mailing address in China, but formatted for the US might produce the words
22982  * "People's Republic of China" in English at the last line of the address, and the 
22983  * Chinese-style address will appear in the first line of the address. In the US, the
22984  * country is on the last line, but in China the country is usually on the first line.
22985  *
22986  * @param {Address} address Address to format
22987  * @returns {string} Returns a string containing the formatted address
22988  */
22989 AddressFmt.prototype.format = function (address) {
22990 	var ret, template, other, format;
22991 	
22992 	if (!address) {
22993 		return "";
22994 	}
22995 	// console.log("formatting address: " + JSON.stringify(address));
22996 	if (address.countryCode && 
22997 			address.countryCode !== this.locale.region && 
22998 			Locale._isRegionCode(this.locale.region) && 
22999 			this.locale.region !== "XX") {
23000 		// we are formatting an address that is sent from this country to another country,
23001 		// so only the country should be in this locale, and the rest should be in the other
23002 		// locale
23003 		// console.log("formatting for another locale. Loading in its settings: " + address.countryCode);
23004 		other = new AddressFmt({
23005 			locale: new Locale(address.countryCode), 
23006 			style: this.styleName
23007 		});
23008 		return other.format(address);
23009 	}
23010 	
23011 	if (typeof(this.style) === 'object') {
23012 		format = this.style[address.format || "latin"];
23013 	} else {
23014 		format = this.style;
23015 	}
23016 	
23017 	// console.log("Using format: " + format);
23018 	// make sure we have a blank string for any missing parts so that
23019 	// those template parts get blanked out
23020 	var params = {
23021 		country: address.country || "",
23022 		region: address.region || "",
23023 		locality: address.locality || "",
23024 		streetAddress: address.streetAddress || "",
23025 		postalCode: address.postalCode || "",
23026 		postOffice: address.postOffice || ""
23027 	};
23028 	template = new IString(format);
23029 	ret = template.format(params);
23030 	ret = ret.replace(/[ \t]+/g, ' ');
23031 	ret = ret.replace("\n ", "\n");
23032 	ret = ret.replace(" \n", "\n");
23033 	return ret.replace(/\n+/g, '\n').trim();
23034 };
23035 
23036 
23037 /**
23038  * Return true if this is an asian locale.
23039  * @private
23040  * @returns {boolean} true if this is an asian locale, or false otherwise
23041  */
23042 function isAsianLocale(locale) {
23043     return locale.language === "zh" || locale.language === "ja" || locale.language === "ko";
23044 }
23045 
23046 /**
23047  * Invert the properties and values, filtering out all the regions. Regions either
23048  * have values with numbers (eg. "150" for Europe), or they are on a short list of
23049  * known regions with actual ISO codes.
23050  * 
23051  * @private
23052  * @returns {Object} the inverted object
23053  */
23054 function invertAndFilter(object) {
23055     var ret = [];
23056     var regions = ["AQ", "EU", "EZ", "UN", "ZZ"]
23057     for (var p in object) {
23058         if (p && !object[p].match(/\d/) && regions.indexOf(object[p]) === -1) {
23059             ret.push({
23060                 code: object[p],
23061                 name: p
23062             });
23063         }
23064     }
23065 
23066     return ret;
23067 }
23068 
23069 /**
23070  * Return information about the address format that can be used
23071  * by UI builders to display a locale-sensitive set of input fields
23072  * based on the current formatter's settings.<p>
23073  *
23074  * The object returned by this method is an array of address rows. Each
23075  * row is itself an array which may have one to four address
23076  * components in that row. Each address component is an object
23077  * that contains a component property and a label to display
23078  * with it. The label is written in the given locale, or the
23079  * locale of this formatter if it was not given.<p>
23080  *
23081  * Optionally, if the address component is constrained to a
23082  * particular pattern or to a fixed list of possible values, then
23083  * the constraint rules are given in the "constraint" property.<p>
23084  *
23085  * If an address component must conform to a particular pattern, 
23086  * the regular expression that matches that pattern
23087  * is returned in "constraint". Mostly, it is only the postal code
23088  * component that can be validated in this way.<p>
23089  *
23090  * If an address component should be limited
23091  * to a fixed list of values, then the constraint property will be
23092  * set to an array that lists those values. The constraint contains
23093  * an array of objects in the correct sorted order for the locale
23094  * where each object contains an code property containing the ISO code, 
23095  * and a name field to show in UI.
23096  * The ISO codes should not be shown to the user and are intended to
23097  * represent the values in code. The names are translated to the given
23098  * locale or to the locale of this formatter if it was not given. For
23099  * the most part, it is the region and country components that
23100  * are constrained in this way.<p>
23101  *
23102  * Here is what the result would look like for a US address:
23103  * <pre>
23104  * [
23105  *   [{
23106  *     "component": "streetAddress",
23107  *     "label": "Street Address"
23108  *   }],
23109  *   [{
23110  *     "component": "locality",
23111  *     "label": "City"
23112  *   },{
23113  *     "component": "region",
23114  *     "label": "State",
23115  *     "constraint": [{
23116  *       "code": "AL",
23117  *       "name": "Alabama"
23118  *     },{
23119  *       "code": "AK",
23120  *       "name": "Alaska"
23121  *     },{
23122  *       ...
23123  *     },{
23124  *       "code": "WY",
23125  *       "name": "Wyoming"
23126  *     }
23127  *   },{
23128  *     "component": "postalCode",
23129  *     "label": "Zip Code",
23130  *     "constraint": "[0-9]{5}(-[0-9]{4})?"
23131  *   }],
23132  *   [{
23133  *     "component": "country",
23134  *     "label": "Country",
23135  *     "constraint": [{
23136  *         "code": "AF",
23137  *         "name": "Afghanistan"
23138  *       },{
23139  *         "code": "AL",
23140  *         "name": "Albania"
23141  *       },{
23142  *       ...
23143  *       },{
23144  *         "code": "ZW",
23145  *         "name": "Zimbabwe"
23146  *     }]
23147  *   }]
23148  * ]
23149  * </pre>
23150  * <p>
23151  * @example <caption>Example of calling the getFormatInfo method</caption>
23152  * 
23153  * // the AddressFmt should be created with the locale of the address you 
23154  * // would like the user to enter. For example, if you have a "country"
23155  * // selector, you would create a new AddressFmt instance each time the
23156  * // selector is changed.
23157  * new AddressFmt({
23158  *   locale: 'nl-NL', // for addresses in the Netherlands
23159  *   onLoad: ilib.bind(this, function(fmt) {
23160  *     fmt.getAddressFormatInfo({
23161  *       // The following is the locale of the UI you would like to see the labels
23162  *       // like "City" and "Postal Code" translated to. In this example, we
23163  *       // are showing an input form for Dutch addresses, but the labels are
23164  *       // written in US English.
23165  *       locale: "en-US", 
23166  *       onLoad: ilib.bind(this, function(rows) {
23167  *         // iterate through the rows array and dynamically create the input 
23168  *         // elements with the given labels
23169  *       })
23170  *     });
23171  *   })
23172  * });
23173  * 
23174  * @param {Locale|string=} locale the locale to translate the labels
23175  * to. If not given, the locale of the formatter will be used.
23176  * @param {boolean=} sync true if this method should load the data
23177  * synchronously, false if async
23178  * @param {Function=} callback a callback to call when the data
23179  * is ready
23180  * @returns {Array.<Object>} An array of rows of address components
23181  */
23182 AddressFmt.prototype.getFormatInfo = function(locale, sync, callback) {
23183     var info;
23184     var loc = new Locale(this.locale);
23185     if (locale) {
23186         if (typeof(locale) === "string") {
23187             locale = new Locale(locale);
23188         }
23189         loc.language = locale.getLanguage();
23190         loc.spec = undefined;
23191     }
23192 
23193     Utils.loadData({
23194         name: "regionnames.json",
23195         object: "AddressFmt",
23196         locale: loc,
23197         sync: this.sync,
23198         loadParams: JSUtils.merge(this.loadParams, {returnOne: true}, true),
23199         callback: ilib.bind(this, function(regions) {
23200             this.regions = regions;
23201 
23202             new ResBundle({
23203                 locale: loc,
23204                 name: "addressres",
23205                 sync: this.sync,
23206                 loadParams: this.loadParams, 
23207                 onLoad: ilib.bind(this, function (rb) {
23208                     var type, format, fields = this.info.fields;
23209                     if (this.info.multiformat) {
23210                         type = isAsianLocale(this.locale) ? "asian" : "latin";
23211                         fields = this.info.fields[type];
23212                     }
23213 
23214                     if (typeof(this.style) === 'object') {
23215                         format = this.style[type || "latin"];
23216                     } else {
23217                         format = this.style;
23218                     }
23219                     new Address(" ", {
23220                         locale: loc,
23221                         sync: this.sync,
23222                         loadParams: this.loadParams,
23223                         onLoad: ilib.bind(this, function(localeAddress) {
23224                             var rows = format.split(/\n/g);
23225                             info = rows.map(ilib.bind(this, function(row) {
23226                                 return row.split("}").filter(function(component) {
23227                                     return component.length > 0;
23228                                 }).map(ilib.bind(this, function(component) {
23229                                     var name = component.replace(/.*{/, "");
23230                                     var obj = {
23231                                         component: name,
23232                                         label: rb.getStringJS(this.info.fieldNames[name])
23233                                     };
23234                                     var field = fields.filter(function(f) {
23235                                         return f.name === name;
23236                                     });
23237                                     if (field && field[0] && field[0].pattern) {
23238                                         if (typeof(field[0].pattern) === "string") {
23239                                             obj.constraint = field[0].pattern;
23240                                         }
23241                                     }
23242                                     if (name === "country") {
23243                                         obj.constraint = invertAndFilter(localeAddress.ctrynames);
23244                                     } else if (name === "region" && this.regions[loc.getRegion()]) {
23245                                         obj.constraint = this.regions[loc.getRegion()];
23246                                     }
23247                                     return obj;
23248                                 }));
23249                             }));
23250                             
23251                             if (callback && typeof(callback) === "function") {
23252                                 callback(info);
23253                             }
23254                         })
23255                     });
23256                 })
23257             });
23258         })
23259     });
23260 
23261     return info;
23262 };
23263 
23264 
23265 
23266 /*< GlyphString.js */
23267 /*
23268  * GlyphString.js - ilib string subclass that allows you to access 
23269  * whole glyphs at a time
23270  * 
23271  * Copyright © 2015-2018, JEDLSoft
23272  *
23273  * Licensed under the Apache License, Version 2.0 (the "License");
23274  * you may not use this file except in compliance with the License.
23275  * You may obtain a copy of the License at
23276  *
23277  *     http://www.apache.org/licenses/LICENSE-2.0
23278  *
23279  * Unless required by applicable law or agreed to in writing, software
23280  * distributed under the License is distributed on an "AS IS" BASIS,
23281  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23282  *
23283  * See the License for the specific language governing permissions and
23284  * limitations under the License.
23285  */
23286 
23287 // !depends IString.js CType.js Utils.js JSUtils.js
23288 // !data normdata ctype_m
23289 
23290 
23291 
23292 /**
23293  * @class
23294  * Create a new glyph string instance. This string inherits from 
23295  * the IString class, and adds methods that allow you to access
23296  * whole glyphs at a time. <p>
23297  * 
23298  * In Unicode, various accented characters can be created by using
23299  * a base character and one or more combining characters following
23300  * it. These appear on the screen to the user as a single glyph.
23301  * For example, the Latin character "a" (U+0061) followed by the
23302  * combining diaresis character "¨" (U+0308) combine together to
23303  * form the "a with diaresis" glyph "ä", which looks like a single
23304  * character on the screen.<p>
23305  * 
23306  * The big problem with combining characters for web developers is
23307  * that many CSS engines do not ellipsize text between glyphs. They
23308  * only deal with single Unicode characters. So if a particular space 
23309  * only allows for 4 characters, the CSS engine will truncate a
23310  * string at 4 Unicode characters and then add the ellipsis (...)
23311  * character. What if the fourth Unicode character is the "a" and
23312  * the fifth one is the diaresis? Then a string like "xxxäxxx" that
23313  * is ellipsized at 4 characters will appear as "xxxa..." on the 
23314  * screen instead of "xxxä...".<p>
23315  * 
23316  * In the Latin script as it is commonly used, it is not so common
23317  * to form accented characters using combining accents, so the above
23318  * example is mostly for illustrative purposes. It is not unheard of
23319  * however. The situation is much, much worse in scripts such as Thai and 
23320  * Devanagari that normally make very heavy use of combining characters.
23321  * These scripts do so because Unicode does not include pre-composed 
23322  * versions of the accented characters like it does for Latin, so 
23323  * combining accents are the only way to create these accented and 
23324  * combined versions of the characters.<p>
23325  * 
23326  * The solution to this problem is not to use the the CSS property 
23327  * "text-overflow: ellipsis" in your web site, ever. Instead, use
23328  * a glyph string to truncate text between glyphs dynamically,
23329  * rather than truncating between Unicode characters using CSS.<p>
23330  * 
23331  * Glyph strings are also useful for truncation, hyphenation, and 
23332  * line wrapping, as all of these should be done between glyphs instead
23333  * of between characters.<p>
23334  * 
23335  * The options parameter is optional, and may contain any combination
23336  * of the following properties:<p>
23337  * 
23338  * <ul>
23339  * <li><i>onLoad</i> - a callback function to call when the locale data are
23340  * fully loaded. When the onLoad option is given, this object will attempt to
23341  * load any missing locale data using the ilib loader callback.
23342  * When the constructor is done (even if the data is already preassembled), the 
23343  * onLoad function is called with the current instance as a parameter, so this
23344  * callback can be used with preassembled or dynamic loading or a mix of the two.
23345  * 
23346  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
23347  * asynchronously. If this option is given as "false", then the "onLoad"
23348  * callback must be given, as the instance returned from this constructor will
23349  * not be usable for a while.
23350  *  
23351  * <li><i>loadParams</i> - an object containing parameters to pass to the 
23352  * loader callback function when locale data is missing. The parameters are not
23353  * interpretted or modified in any way. They are simply passed along. The object 
23354  * may contain any property/value pairs as long as the calling code is in
23355  * agreement with the loader callback function as to what those parameters mean.
23356  * </ul>
23357  * 
23358  * @constructor
23359  * @extends IString
23360  * @param {string|IString=} str initialize this instance with this string 
23361  * @param {Object=} options options governing the way this instance works
23362  */
23363 var GlyphString = function (str, options) {
23364 	if (options && options.noinstance) {
23365 		return;
23366 	}
23367 	
23368 	IString.call(this, str);
23369 	
23370 	options = options || {sync: true};
23371 	
23372 	CType._load("ctype_m", options.sync, options.loadParams, ilib.bind(this, function() {
23373 		if (!ilib.data.norm || JSUtils.isEmpty(ilib.data.norm.ccc)) {
23374 			Utils.loadData({
23375 				object: "GlyphString", 
23376 				locale: "-", 
23377 				name: "normdata.json",
23378 				nonlocale: true,
23379 				sync: options.sync, 
23380 				loadParams: options.loadParams, 
23381 				callback: ilib.bind(this, function (norm) {
23382 					ilib.extend(ilib.data.norm, norm);
23383 					if (options && typeof(options.onLoad) === 'function') {
23384 						options.onLoad(this);
23385 					}
23386 				})
23387 			});
23388 		} else {
23389 			if (options && typeof(options.onLoad) === 'function') {
23390 				options.onLoad(this);
23391 			}
23392 		}
23393 	}));
23394 };
23395 
23396 GlyphString.prototype = new IString(undefined);
23397 GlyphString.prototype.parent = IString;
23398 GlyphString.prototype.constructor = GlyphString;
23399 
23400 /**
23401  * Return true if the given character is a leading Jamo (Choseong) character.
23402  * 
23403  * @private
23404  * @static
23405  * @param {number} n code point to check
23406  * @return {boolean} true if the character is a leading Jamo character, 
23407  * false otherwise
23408  */
23409 GlyphString._isJamoL = function (n) {
23410 	return (n >= 0x1100 && n <= 0x1112);
23411 };
23412 
23413 /**
23414  * Return true if the given character is a vowel Jamo (Jungseong) character.
23415  * 
23416  * @private
23417  * @static
23418  * @param {number} n code point to check
23419  * @return {boolean} true if the character is a vowel Jamo character, 
23420  * false otherwise
23421  */
23422 GlyphString._isJamoV = function (n) {
23423 	return (n >= 0x1161 && n <= 0x1175);
23424 };
23425 
23426 /**
23427  * Return true if the given character is a trailing Jamo (Jongseong) character.
23428  * 
23429  * @private
23430  * @static
23431  * @param {number} n code point to check
23432  * @return {boolean} true if the character is a trailing Jamo character, 
23433  * false otherwise
23434  */
23435 GlyphString._isJamoT = function (n) {
23436 	return (n >= 0x11A8 && n <= 0x11C2);
23437 };
23438 
23439 /**
23440  * Return true if the given character is a LV Jamo character.
23441  * LV Jamo character is a precomposed Hangul character with LV sequence.
23442  * 
23443  * @private
23444  * @static
23445  * @param {number} n code point to check
23446  * @return {boolean} true if the character is a LV Jamo character,
23447  * false otherwise
23448  */
23449 GlyphString._isJamoLV = function (n) {
23450 	var syllableBase = 0xAC00;
23451 	var leadingJamoCount = 19;
23452 	var vowelJamoCount = 21;
23453 	var trailingJamoCount = 28;
23454 	var syllableCount = leadingJamoCount * vowelJamoCount * trailingJamoCount;
23455 	var syllableIndex = n - syllableBase;
23456 	// Check if n is a precomposed Hangul
23457 	if (0 <= syllableIndex && syllableIndex < syllableCount) {
23458 	// Check if n is a LV Jamo character
23459 		if((syllableIndex % trailingJamoCount) == 0) {
23460 			return true;
23461 		}
23462 	}
23463 	return false;
23464 };
23465 
23466 /**
23467  * Return true if the given character is a precomposed Hangul character.
23468  * The precomposed Hangul character may be a LV Jamo character or a LVT Jamo Character.
23469  * 
23470  * @private
23471  * @static
23472  * @param {number} n code point to check
23473  * @return {boolean} true if the character is a precomposed Hangul character, 
23474  * false otherwise
23475  */
23476 GlyphString._isHangul = function (n) {
23477 	return (n >= 0xAC00 && n <= 0xD7A3);
23478 };
23479 
23480 /**
23481  * Algorithmically compose an L and a V combining Jamo characters into
23482  * a precomposed Korean syllabic Hangul character. Both should already
23483  * be in the proper ranges for L and V characters. 
23484  * 
23485  * @private
23486  * @static
23487  * @param {number} lead the code point of the lead Jamo character to compose
23488  * @param {number} trail the code point of the trailing Jamo character to compose
23489  * @return {string} the composed Hangul character
23490  */
23491 GlyphString._composeJamoLV = function (lead, trail) {
23492 	var lindex = lead - 0x1100;
23493 	var vindex = trail - 0x1161;
23494 	return IString.fromCodePoint(0xAC00 + (lindex * 21 + vindex) * 28);
23495 };
23496 
23497 /**
23498  * Algorithmically compose a Hangul LV and a combining Jamo T character 
23499  * into a precomposed Korean syllabic Hangul character. 
23500  * 
23501  * @private
23502  * @static
23503  * @param {number} lead the code point of the lead Hangul character to compose
23504  * @param {number} trail the code point of the trailing Jamo T character to compose
23505  * @return {string} the composed Hangul character
23506  */
23507 GlyphString._composeJamoLVT = function (lead, trail) {
23508 	return IString.fromCodePoint(lead + (trail - 0x11A7));
23509 };
23510 
23511 /**
23512  * Compose one character out of a leading character and a 
23513  * trailing character. If the characters are Korean Jamo, they
23514  * will be composed algorithmically. If they are any other
23515  * characters, they will be looked up in the nfc tables.
23516  * 
23517  * @private
23518  * @static
23519  * @param {string} lead leading character to compose
23520  * @param {string} trail the trailing character to compose
23521  * @return {string|null} the fully composed character, or undefined if
23522  * there is no composition for those two characters
23523  */
23524 GlyphString._compose = function (lead, trail) {
23525 	var first = lead.charCodeAt(0);
23526 	var last = trail.charCodeAt(0);
23527 	if (GlyphString._isJamoLV(first) && GlyphString._isJamoT(last)) {
23528 		return GlyphString._composeJamoLVT(first, last);
23529 	} else if (GlyphString._isJamoL(first) && GlyphString._isJamoV(last)) {
23530 		return GlyphString._composeJamoLV(first, last);
23531 	}
23532 
23533 	var c = lead + trail;
23534 	return (ilib.data.norm.nfc && ilib.data.norm.nfc[c]);
23535 };
23536 
23537 /**
23538  * Return an iterator that will step through all of the characters
23539  * in the string one at a time, taking care to step through decomposed 
23540  * characters and through surrogate pairs in the UTF-16 encoding 
23541  * as single characters. <p>
23542  * 
23543  * The GlyphString class will return decomposed Unicode characters
23544  * as a single unit that a user might see on the screen as a single
23545  * glyph. If the 
23546  * next character in the iteration is a base character and it is 
23547  * followed by combining characters, the base and all its following 
23548  * combining characters are returned as a single unit.<p>
23549  * 
23550  * The standard Javascript String's charAt() method only
23551  * returns information about a particular 16-bit character in the 
23552  * UTF-16 encoding scheme.
23553  * If the index is pointing to a low- or high-surrogate character,
23554  * it will return that surrogate character rather 
23555  * than the surrogate pair which represents a character 
23556  * in the supplementary planes.<p>
23557  * 
23558  * The iterator instance returned has two methods, hasNext() which
23559  * returns true if the iterator has more characters to iterate through,
23560  * and next() which returns the next character.<p>
23561  * 
23562  * @override
23563  * @return {Object} an iterator 
23564  * that iterates through all the characters in the string
23565  */
23566 GlyphString.prototype.charIterator = function() {
23567 	var it = IString.prototype.charIterator.call(this);
23568 	
23569 	/**
23570 	 * @constructor
23571 	 */
23572 	function _chiterator (istring) {
23573 		this.index = 0;
23574 		this.spacingCombining = false;
23575 		this.hasNext = function () {
23576 			return !!this.nextChar || it.hasNext();
23577 		};
23578 		this.next = function () {
23579 			var ch = this.nextChar || it.next(),
23580 				prevCcc = ilib.data.norm.ccc[ch],
23581 				nextCcc,
23582 				composed = ch;
23583 			
23584 			this.nextChar = undefined;
23585 			this.spacingCombining = false;
23586 			
23587 			if (ilib.data.norm.ccc && 
23588 					(typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ilib.data.norm.ccc[ch] === 0)) {
23589 				// found a starter... find all the non-starters until the next starter. Must include
23590 				// the next starter because under some odd circumstances, two starters sometimes recompose 
23591 				// together to form another character
23592 				var notdone = true;
23593 				while (it.hasNext() && notdone) {
23594 					this.nextChar = it.next();
23595 					nextCcc = ilib.data.norm.ccc[this.nextChar];
23596 					var codePoint = IString.toCodePoint(this.nextChar, 0);
23597 					// Mn characters are Marks that are non-spacing. These do not take more room than an accent, so they should be 
23598 					// considered part of the on-screen glyph, even if they are non-combining. Mc are marks that are spacing
23599 					// and combining, which means they are part of the glyph, but they cause the glyph to use up more space than
23600 					// just the base character alone.
23601 					var isMn = CType._inRange(codePoint, "Mn", ilib.data.ctype_m);
23602 					var isMc = CType._inRange(codePoint, "Mc", ilib.data.ctype_m);
23603 					if (isMn || isMc || (typeof(nextCcc) !== 'undefined' && nextCcc !== 0)) {
23604 						if (isMc) {
23605 							this.spacingCombining = true;
23606 						}
23607 						ch += this.nextChar;
23608 						this.nextChar = undefined;
23609 					} else {
23610 						// found the next starter. See if this can be composed with the previous starter
23611 						var testChar = GlyphString._compose(composed, this.nextChar);
23612 						if (prevCcc === 0 && typeof(testChar) !== 'undefined') { 
23613 							// not blocked and there is a mapping 
23614 							composed = testChar;
23615 							ch += this.nextChar;
23616 							this.nextChar = undefined;
23617 						} else {
23618 							// finished iterating, leave this.nextChar for the next next() call 
23619 							notdone = false;
23620 						}
23621 					}
23622 					prevCcc = nextCcc;
23623 				}
23624 			}
23625 			return ch;
23626 		};
23627 		// Returns true if the last character returned by the "next" method included
23628 		// spacing combining characters. If it does, then the character was wider than
23629 		// just the base character alone, and the truncation code will not add it.
23630 		this.wasSpacingCombining = function() {
23631 			return this.spacingCombining;
23632 		};
23633 	};
23634 	return new _chiterator(this);
23635 };
23636 
23637 /**
23638  * Truncate the current string at the given number of whole glyphs and return
23639  * the resulting string.
23640  * 
23641  * @param {number} length the number of whole glyphs to keep in the string
23642  * @return {string} a string truncated to the requested number of glyphs
23643  */
23644 GlyphString.prototype.truncate = function(length) {
23645 	var it = this.charIterator();
23646 	var tr = "";
23647 	for (var i = 0; i < length-1 && it.hasNext(); i++) {
23648 		tr += it.next();
23649 	}
23650 	
23651 	/*
23652 	 * handle the last character separately. If it contains spacing combining
23653 	 * accents, then we must assume that it uses up more horizontal space on
23654 	 * the screen than just the base character by itself, and therefore this
23655 	 * method will not truncate enough characters to fit in the given length.
23656 	 * In this case, we have to chop off not only the combining characters, 
23657 	 * but also the base character as well because the base without the
23658 	 * combining accents is considered a different character.
23659 	 */
23660 	if (i < length && it.hasNext()) {
23661 		var c = it.next();
23662 		if (!it.wasSpacingCombining()) {
23663 			tr += c;
23664 		}
23665 	}
23666 	return tr;
23667 };
23668 
23669 /**
23670  * Truncate the current string at the given number of glyphs and add an ellipsis
23671  * to indicate that is more to the string. The ellipsis forms the last character
23672  * in the string, so the string is actually truncated at length-1 glyphs.
23673  * 
23674  * @param {number} length the number of whole glyphs to keep in the string 
23675  * including the ellipsis
23676  * @return {string} a string truncated to the requested number of glyphs
23677  * with an ellipsis
23678  */
23679 GlyphString.prototype.ellipsize = function(length) {
23680 	return this.truncate(length > 0 ? length-1 : 0) + "…";
23681 };
23682 
23683 
23684 
23685 /*< NormString.js */
23686 /*
23687  * NormString.js - ilib normalized string subclass definition
23688  *
23689  * Copyright © 2013-2015, JEDLSoft
23690  *
23691  * Licensed under the Apache License, Version 2.0 (the "License");
23692  * you may not use this file except in compliance with the License.
23693  * You may obtain a copy of the License at
23694  *
23695  *     http://www.apache.org/licenses/LICENSE-2.0
23696  *
23697  * Unless required by applicable law or agreed to in writing, software
23698  * distributed under the License is distributed on an "AS IS" BASIS,
23699  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23700  *
23701  * See the License for the specific language governing permissions and
23702  * limitations under the License.
23703  */
23704 
23705 // !depends IString.js GlyphString.js Utils.js
23706 
23707 
23708 
23709 /**
23710  * @class
23711  * Create a new normalized string instance. This string inherits from
23712  * the GlyphString class, and adds the normalize method. It can be
23713  * used anywhere that a normal Javascript string is used. <p>
23714  *
23715  * The options parameter is optional, and may contain any combination
23716  * of the following properties:<p>
23717  *
23718  * <ul>
23719  * <li><i>onLoad</i> - a callback function to call when the locale data are
23720  * fully loaded. When the onLoad option is given, this object will attempt to
23721  * load any missing locale data using the ilib loader callback.
23722  * When the constructor is done (even if the data is already preassembled), the
23723  * onLoad function is called with the current instance as a parameter, so this
23724  * callback can be used with preassembled or dynamic loading or a mix of the two.
23725  *
23726  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
23727  * asynchronously. If this option is given as "false", then the "onLoad"
23728  * callback must be given, as the instance returned from this constructor will
23729  * not be usable for a while.
23730  *
23731  * <li><i>loadParams</i> - an object containing parameters to pass to the
23732  * loader callback function when locale data is missing. The parameters are not
23733  * interpretted or modified in any way. They are simply passed along. The object
23734  * may contain any property/value pairs as long as the calling code is in
23735  * agreement with the loader callback function as to what those parameters mean.
23736  * </ul>
23737  *
23738  * @constructor
23739  * @extends GlyphString
23740  * @param {string|IString=} str initialize this instance with this string
23741  * @param {Object=} options options governing the way this instance works
23742  */
23743 var NormString = function (str, options) {
23744     GlyphString.call(this, str, options);
23745 };
23746 
23747 NormString.prototype = new GlyphString("", {noinstance:true});
23748 NormString.prototype.parent = GlyphString;
23749 NormString.prototype.constructor = NormString;
23750 
23751 /**
23752  * Initialize the normalized string routines statically. This
23753  * is intended to be called in a dynamic-load version of ilib
23754  * to load the data need to normalize strings before any instances
23755  * of NormString are created.<p>
23756  *
23757  * The options parameter may contain any of the following properties:
23758  *
23759  * <ul>
23760  * <li><i>form</i> - {string} the normalization form to load
23761  * <li><i>script</i> - {string} load the normalization for this script. If the
23762  * script is given as "all" then the normalization data for all scripts
23763  * is loaded at the same time
23764  * <li><i>sync</i> - {boolean} whether to load the files synchronously or not
23765  * <li><i>loadParams</i> - {Object} parameters to the loader function
23766  * <li><i>onLoad</i> - {function()} a function to call when the
23767  * files are done being loaded
23768  * </ul>
23769  *
23770  * @param {Object} options an object containing properties that govern
23771  * how to initialize the data
23772  */
23773 
23774 NormString.init = function(options) {
23775     var form = "nfkc";
23776     var script = "all";
23777     var sync = true;
23778     var loadParams = undefined;
23779 
23780     if (options) {
23781         if (options.form) {
23782             form = options.form;
23783         }
23784         if (options.script) {
23785             script = options.script;
23786         }
23787         if (options.loadParams) {
23788             loadParams = options.loadParams;
23789         }
23790         if (typeof(options.sync) === 'boolean') {
23791             sync = options.sync;
23792         }
23793     }
23794 
23795     var formDependencies = {
23796         "nfd": ["nfd"],
23797         "nfc": ["nfd"],
23798         "nfkd": ["nfkd", "nfd"],
23799         "nfkc": ["nfkd", "nfd"]
23800     };
23801     var files = ["normdata.json"];
23802     var forms = formDependencies[form];
23803     for (var f in forms) {
23804         files.push(forms[f] + "/" + script + ".json");
23805     }
23806 
23807     if (!ilib.data.norm || JSUtils.isEmpty(ilib.data.norm.ccc)) {
23808         Utils.loadData({
23809             object: "NormString",
23810             name: "normdata.json",
23811             locale: "-",
23812             nonlocale: true,
23813             sync: sync,
23814             loadParams: loadParams,
23815             callback: ilib.bind(this, function(normdata) {
23816                 if (!normdata) {
23817                     ilib.data.cache.normdata = normdata;
23818                 }
23819 
23820                 if (JSUtils.isEmpty(ilib.data.norm.ccc) || JSUtils.isEmpty(ilib.data.norm.nfd) || JSUtils.isEmpty(ilib.data.norm.nfkd)) {
23821                     //console.log("loading files " + JSON.stringify(files));
23822                     Utils._callLoadData(files, sync, loadParams, function(arr) {
23823                         ilib.extend(ilib.data.norm, arr[0]);
23824                         for (var i = 1; i < arr.length; i++) {
23825                             if (typeof(arr[i]) !== 'undefined') {
23826                                 ilib.extend(ilib.data.norm[forms[i-1]], arr[i]);
23827                             }
23828                         }
23829                         if (options && typeof(options.onLoad) === 'function') {
23830                            options.onLoad(this);
23831                         }
23832                     });
23833                 } else {
23834                     if (options && typeof(options.onLoad) === 'function') {
23835                         options.onLoad(this);
23836                     }
23837                 }
23838             })
23839         })
23840     } else {
23841         if (options && typeof(options.onLoad) === 'function') {
23842             options.onLoad(this);
23843         }
23844     }
23845 }
23846 
23847 /**
23848  * Algorithmically decompose a precomposed Korean syllabic Hangul
23849  * character into its individual combining Jamo characters. The given
23850  * character must be in the range of Hangul characters U+AC00 to U+D7A3.
23851  *
23852  * @private
23853  * @static
23854  * @param {number} cp code point of a Korean Hangul character to decompose
23855  * @return {string} the decomposed string of Jamo characters
23856  */
23857 NormString._decomposeHangul = function (cp) {
23858     var sindex = cp - 0xAC00;
23859     var result = String.fromCharCode(0x1100 + sindex / 588) +
23860             String.fromCharCode(0x1161 + (sindex % 588) / 28);
23861     var t = sindex % 28;
23862     if (t !== 0) {
23863         result += String.fromCharCode(0x11A7 + t);
23864     }
23865     return result;
23866 };
23867 
23868 /**
23869  * Expand one character according to the given canonical and
23870  * compatibility mappings.
23871  *
23872  * @private
23873  * @static
23874  * @param {string} ch character to map
23875  * @param {Object} canon the canonical mappings to apply
23876  * @param {Object=} compat the compatibility mappings to apply, or undefined
23877  * if only the canonical mappings are needed
23878  * @return {string} the mapped character
23879  */
23880 NormString._expand = function (ch, canon, compat) {
23881     var i,
23882         expansion = "",
23883         n = ch.charCodeAt(0);
23884     if (GlyphString._isHangul(n)) {
23885         expansion = NormString._decomposeHangul(n);
23886     } else {
23887         var result = canon[ch];
23888         if (!result && compat) {
23889             result = compat[ch];
23890         }
23891         if (result && result !== ch) {
23892             for (i = 0; i < result.length; i++) {
23893                 expansion += NormString._expand(result[i], canon, compat);
23894             }
23895         } else {
23896             expansion = ch;
23897         }
23898     }
23899     return expansion;
23900 };
23901 
23902 /**
23903  * Perform the Unicode Normalization Algorithm upon the string and return
23904  * the resulting new string. The current string is not modified.
23905  *
23906  * <h2>Forms</h2>
23907  *
23908  * The forms of possible normalizations are defined by the <a
23909  * href="http://www.unicode.org/reports/tr15/">Unicode Standard
23910  * Annex (UAX) 15</a>. The form parameter is a string that may have one
23911  * of the following values:
23912  *
23913  * <ul>
23914  * <li>nfd - Canonical decomposition. This decomposes characters into
23915  * their exactly equivalent forms. For example, "ü" would decompose
23916  * into a "u" followed by the combining diaeresis character.
23917  * <li>nfc - Canonical decomposition followed by canonical composition.
23918  * This decomposes and then recomposes character into their shortest
23919  * exactly equivalent forms by recomposing as many combining characters
23920  * as possible. For example, "ü" followed by a combining
23921  * macron character would decompose into a "u" followed by the combining
23922  * macron characters the combining diaeresis character, and then be recomposed into
23923  * the u with macron and diaeresis "ṻ" character. The reason that
23924  * the "nfc" form decomposes and then recomposes is that combining characters
23925  * have a specific order under the Unicode Normalization Algorithm, and
23926  * partly composed characters such as the "ü" followed by combining
23927  * marks may change the order of the combining marks when decomposed and
23928  * recomposed.
23929  * <li>nfkd - Compatibility decomposition. This decomposes characters
23930  * into compatible forms that may not be exactly equivalent semantically,
23931  * as well as performing canonical decomposition as well.
23932  * For example, the "œ" ligature character decomposes to the two
23933  * characters "oe" because they are compatible even though they are not
23934  * exactly the same semantically.
23935  * <li>nfkc - Compatibility decomposition followed by canonical composition.
23936  * This decomposes characters into compatible forms, then recomposes
23937  * characters using the canonical composition. That is, it breaks down
23938  * characters into the compatible forms, and then recombines all combining
23939  * marks it can with their base characters. For example, the character
23940  * "ǽ" would be normalized to "aé" by first decomposing
23941  * the character into "a" followed by "e" followed by the combining acute accent
23942  * combining mark, and then recomposed to an "a" followed by the "e"
23943  * with acute accent.
23944  * </ul>
23945  *
23946  * <h2>Operation</h2>
23947  *
23948  * Two strings a and b can be said to be canonically equivalent if
23949  * normalize(a) = normalize(b)
23950  * under the nfc normalization form. Two strings can be said to be compatible if
23951  * normalize(a) = normalize(b) under the nfkc normalization form.<p>
23952  *
23953  * The canonical normalization is often used to see if strings are
23954  * equivalent to each other, and thus is useful when implementing parsing
23955  * algorithms or exact matching algorithms. It can also be used to ensure
23956  * that any string output produces a predictable sequence of characters.<p>
23957  *
23958  * Compatibility normalization
23959  * does not always preserve the semantic meaning of all the characters,
23960  * although this is sometimes the behaviour that you are after. It is useful,
23961  * for example, when doing searches of user-input against text in documents
23962  * where the matches are supposed to "fuzzy". In this case, both the query
23963  * string and the document string would be mapped to their compatibility
23964  * normalized forms, and then compared.<p>
23965  *
23966  * Compatibility normalization also does not guarantee round-trip conversion
23967  * to and from legacy character sets as the normalization is "lossy". It is
23968  * akin to doing a lower- or upper-case conversion on text -- after casing,
23969  * you cannot tell what case each character is in the original string. It is
23970  * good for matching and searching, but it rarely good for output because some
23971  * distinctions or meanings in the original text have been lost.<p>
23972  *
23973  * Note that W3C normalization for HTML also escapes and unescapes
23974  * HTML character entities such as "&uuml;" for u with diaeresis. This
23975  * method does not do such escaping or unescaping. If normalization is required
23976  * for HTML strings with entities, unescaping should be performed on the string
23977  * prior to calling this method.<p>
23978  *
23979  * <h2>Data</h2>
23980  *
23981  * Normalization requires a fair amount of mapping data, much of which you may
23982  * not need for the characters expected in your texts. It is possible to assemble
23983  * a copy of ilib that saves space by only including normalization data for
23984  * those scripts that you expect to encounter in your data.<p>
23985  *
23986  * The normalization data is organized by normalization form and within there
23987  * by script. To include the normalization data for a particular script with
23988  * a particular normalization form, use the directive:
23989  *
23990  * <pre><code>
23991  * !depends <form>/<script>.js
23992  * </code></pre>
23993  *
23994  * Where <form> is the normalization form ("nfd", "nfc", "nfkd", or "nfkc"), and
23995  * <script> is the ISO 15924 code for the script you would like to
23996  * support. Example: to load in the NFC data for Cyrillic, you would use:
23997  *
23998  * <pre><code>
23999  * !depends nfc/Cyrl.js
24000  * </code></pre>
24001  *
24002  * Note that because certain normalization forms include others in their algorithm,
24003  * their data also depends on the data for the other forms. For example, if you
24004  * include the "nfc" data for a script, you will automatically get the "nfd" data
24005  * for that same script as well because the NFC algorithm does NFD normalization
24006  * first. Here are the dependencies:<p>
24007  *
24008  * <ul>
24009  * <li>NFD -> no dependencies
24010  * <li>NFC -> NFD
24011  * <li>NFKD -> NFD
24012  * <li>NFKC -> NFKD, NFD, NFC
24013  * </ul>
24014  *
24015  * A special value for the script dependency is "all" which will cause the data for
24016  * all scripts
24017  * to be loaded for that normalization form. This would be useful if you know that
24018  * you are going to normalize a lot of multilingual text or cannot predict which scripts
24019  * will appear in the input. Because the NFKC form depends on all others, you can
24020  * get all of the data for all forms automatically by depending on "nfkc/all.js".
24021  * Note that the normalization data for practically all script automatically depend
24022  * on data for the Common script (code "Zyyy") which contains all of the characters
24023  * that are commonly used in many different scripts. Examples of characters in the
24024  * Common script are the ASCII punctuation characters, or the ASCII Arabic
24025  * numerals "0" through "9".<p>
24026  *
24027  * By default, none of the data for normalization is automatically
24028  * included in the preassembled iliball.js file.
24029  * If you would like to normalize strings, you must assemble
24030  * your own copy of ilib and explicitly include the normalization data
24031  * for those scripts as per the instructions above. This normalization method will
24032  * produce output, even without the normalization data. However, the output will be
24033  * simply the same thing as its input for all scripts
24034  * except Korean Hangul and Jamo, which are decomposed and recomposed
24035  * algorithmically and therefore do not rely on data.<p>
24036  *
24037  * If characters are encountered for which there are no normalization data, they
24038  * will be passed through to the output string unmodified.
24039  *
24040  * @param {string} form The normalization form requested
24041  * @return {IString} a new instance of an IString that has been normalized
24042  * according to the requested form. The current instance is not modified.
24043  */
24044 NormString.prototype.normalize = function (form) {
24045     var i;
24046 
24047     if (typeof(form) !== 'string' || this.str.length === 0) {
24048         return new IString(this.str);
24049     }
24050 
24051     var nfc = false,
24052         nfkd = false;
24053 
24054     switch (form) {
24055     default:
24056         break;
24057 
24058     case "nfc":
24059         nfc = true;
24060         break;
24061 
24062     case "nfkd":
24063         nfkd = true;
24064         break;
24065 
24066     case "nfkc":
24067         nfkd = true;
24068         nfc = true;
24069         break;
24070     }
24071 
24072     // decompose
24073     var ch, it, decomp = "";
24074 
24075     if (nfkd) {
24076         it = IString.prototype.charIterator.call(this);
24077         while (it.hasNext()) {
24078             ch = it.next();
24079             decomp += NormString._expand(ch, ilib.data.norm.nfd, ilib.data.norm.nfkd);
24080         }
24081     } else {
24082         it = IString.prototype.charIterator.call(this);
24083         while (it.hasNext()) {
24084             ch = it.next();
24085             decomp += NormString._expand(ch, ilib.data.norm.nfd);
24086         }
24087     }
24088 
24089     // now put the combining marks in a fixed order by
24090     // sorting on the combining class
24091     function compareByCCC(left, right) {
24092         return ilib.data.norm.ccc[left] - ilib.data.norm.ccc[right];
24093     }
24094 
24095     function ccc(c) {
24096         return ilib.data.norm.ccc[c] || 0;
24097     }
24098 
24099     function sortChars(arr, comp) {
24100         // qt/qml's Javascript engine re-arranges entries that are equal to
24101         // each other. Technically, that is a correct behaviour, but it is
24102         // not desirable. All the other engines leave equivalent entries
24103         // where they are. This bubblesort emulates what the other engines
24104         // do. Fortunately, the arrays we are sorting are a max of 5 or 6
24105         // entries, so performance is not a big deal here.
24106         if (ilib._getPlatform() === "qt") {
24107             var tmp;
24108             for (var i = arr.length-1; i > 0; i--) {
24109                 for (var j = 0; j < i; j++) {
24110                     if (comp(arr[j], arr[j+1]) > 0) {
24111                         tmp = arr[j];
24112                         arr[j] = arr[j+1];
24113                         arr[j+1] = tmp;
24114                     }
24115                 }
24116             }
24117             return arr;
24118         } else {
24119             return arr.sort(comp);
24120         }
24121     }
24122 
24123     var dstr = new IString(decomp);
24124     it = dstr.charIterator();
24125     var end, testChar, cpArray = [];
24126 
24127     // easier to deal with as an array of chars
24128     while (it.hasNext()) {
24129         cpArray.push(it.next());
24130     }
24131 
24132     i = 0;
24133     while (i < cpArray.length) {
24134         if (typeof(ilib.data.norm.ccc[cpArray[i]]) !== 'undefined' && ccc(cpArray[i]) !== 0) {
24135             // found a non-starter... rearrange all the non-starters until the next starter
24136             end = i+1;
24137             while (end < cpArray.length &&
24138                     typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' &&
24139                     ccc(cpArray[end]) !== 0) {
24140                 end++;
24141             }
24142 
24143             // simple sort of the non-starter chars
24144             if (end - i > 1) {
24145                 cpArray = cpArray.slice(0,i).concat(sortChars(cpArray.slice(i, end), compareByCCC), cpArray.slice(end));
24146             }
24147         }
24148         i++;
24149     }
24150 
24151     if (nfc) {
24152         i = 0;
24153         while (i < cpArray.length) {
24154             if (typeof(ilib.data.norm.ccc[cpArray[i]]) === 'undefined' || ilib.data.norm.ccc[cpArray[i]] === 0) {
24155                 // found a starter... find all the non-starters until the next starter. Must include
24156                 // the next starter because under some odd circumstances, two starters sometimes recompose
24157                 // together to form another character
24158                 end = i+1;
24159                 var notdone = true;
24160                 while (end < cpArray.length && notdone) {
24161                     if (typeof(ilib.data.norm.ccc[cpArray[end]]) !== 'undefined' &&
24162                         ilib.data.norm.ccc[cpArray[end]] !== 0) {
24163                         if (ccc(cpArray[end-1]) < ccc(cpArray[end])) {
24164                             // not blocked
24165                             testChar = GlyphString._compose(cpArray[i], cpArray[end]);
24166                             if (typeof(testChar) !== 'undefined') {
24167                                 cpArray[i] = testChar;
24168 
24169                                 // delete the combining char
24170                                 cpArray.splice(end,1);
24171 
24172                                 // restart the iteration, just in case there is more to recompose with the new char
24173                                 end = i;
24174                             }
24175                         }
24176                         end++;
24177                     } else {
24178                         // found the next starter. See if this can be composed with the previous starter
24179                         testChar = GlyphString._compose(cpArray[i], cpArray[end]);
24180                         if (ccc(cpArray[end-1]) === 0 && typeof(testChar) !== 'undefined') {
24181                             // not blocked and there is a mapping
24182                             cpArray[i] = testChar;
24183 
24184                             // delete the combining char
24185                             cpArray.splice(end,1);
24186 
24187                             // restart the iteration, just in case there is more to recompose with the new char
24188                             end = i+1;
24189                         } else {
24190                             // finished iterating
24191                             notdone = false;
24192                         }
24193                     }
24194                 }
24195             }
24196             i++;
24197         }
24198     }
24199 
24200     return new IString(cpArray.length > 0 ? cpArray.join("") : "");
24201 };
24202 
24203 /**
24204  * @override
24205  * Return an iterator that will step through all of the characters
24206  * in the string one at a time, taking care to step through decomposed
24207  * characters and through surrogate pairs in UTF-16 encoding
24208  * properly. <p>
24209  *
24210  * The NormString class will return decomposed Unicode characters
24211  * as a single unit that a user might see on the screen. If the
24212  * next character in the iteration is a base character and it is
24213  * followed by combining characters, the base and all its following
24214  * combining characters are returned as a single unit.<p>
24215  *
24216  * The standard Javascript String's charAt() method only
24217  * returns information about a particular 16-bit character in the
24218  * UTF-16 encoding scheme.
24219  * If the index is pointing to a low- or high-surrogate character,
24220  * it will return that surrogate character rather
24221  * than the surrogate pair which represents a character
24222  * in the supplementary planes.<p>
24223  *
24224  * The iterator instance returned has two methods, hasNext() which
24225  * returns true if the iterator has more characters to iterate through,
24226  * and next() which returns the next character.<p>
24227  *
24228  * @return {Object} an iterator
24229  * that iterates through all the characters in the string
24230  */
24231 NormString.prototype.charIterator = function() {
24232     var it = IString.prototype.charIterator.call(this);
24233 
24234     /**
24235      * @constructor
24236      */
24237     function _chiterator (istring) {
24238         /**
24239          * @private
24240          */
24241         var ccc = function(c) {
24242             return ilib.data.norm.ccc[c] || 0;
24243         };
24244 
24245         this.index = 0;
24246         this.hasNext = function () {
24247             return !!this.nextChar || it.hasNext();
24248         };
24249         this.next = function () {
24250             var ch = this.nextChar || it.next(),
24251                 prevCcc = ccc(ch),
24252                 nextCcc,
24253                 composed = ch;
24254 
24255             this.nextChar = undefined;
24256 
24257             if (ilib.data.norm.ccc &&
24258                     (typeof(ilib.data.norm.ccc[ch]) === 'undefined' || ccc(ch) === 0)) {
24259                 // found a starter... find all the non-starters until the next starter. Must include
24260                 // the next starter because under some odd circumstances, two starters sometimes recompose
24261                 // together to form another character
24262                 var notdone = true;
24263                 while (it.hasNext() && notdone) {
24264                     this.nextChar = it.next();
24265                     nextCcc = ccc(this.nextChar);
24266                     if (typeof(ilib.data.norm.ccc[this.nextChar]) !== 'undefined' && nextCcc !== 0) {
24267                         ch += this.nextChar;
24268                         this.nextChar = undefined;
24269                     } else {
24270                         // found the next starter. See if this can be composed with the previous starter
24271                         var testChar = GlyphString._compose(composed, this.nextChar);
24272                         if (prevCcc === 0 && typeof(testChar) !== 'undefined') {
24273                             // not blocked and there is a mapping
24274                             composed = testChar;
24275                             ch += this.nextChar;
24276                             this.nextChar = undefined;
24277                         } else {
24278                             // finished iterating, leave this.nextChar for the next next() call
24279                             notdone = false;
24280                         }
24281                     }
24282                     prevCcc = nextCcc;
24283                 }
24284             }
24285             return ch;
24286         };
24287     };
24288     return new _chiterator(this);
24289 };
24290 
24291 
24292 
24293 /*< CodePointSource.js */
24294 /*
24295  * CodePointSource.js - Source of code points from a string
24296  * 
24297  * Copyright © 2013-2015, JEDLSoft
24298  *
24299  * Licensed under the Apache License, Version 2.0 (the "License");
24300  * you may not use this file except in compliance with the License.
24301  * You may obtain a copy of the License at
24302  *
24303  *     http://www.apache.org/licenses/LICENSE-2.0
24304  *
24305  * Unless required by applicable law or agreed to in writing, software
24306  * distributed under the License is distributed on an "AS IS" BASIS,
24307  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24308  *
24309  * See the License for the specific language governing permissions and
24310  * limitations under the License.
24311  */
24312 
24313 // !depends isPunct.js NormString.js
24314 
24315 
24316 /**
24317  * @class
24318  * Represents a buffered source of code points. The input string is first
24319  * normalized so that combining characters come out in a standardized order.
24320  * If the "ignorePunctuation" flag is turned on, then punctuation 
24321  * characters are skipped.
24322  * 
24323  * @constructor
24324  * @private
24325  * @param {NormString|string} str a string to get code points from
24326  * @param {boolean} ignorePunctuation whether or not to ignore punctuation
24327  * characters
24328  */
24329 var CodePointSource = function(str, ignorePunctuation) {
24330 	this.chars = [];
24331 	// first convert the string to a normalized sequence of characters
24332 	var s = (typeof(str) === "string") ? new NormString(str) : str;
24333 	this.it = s.charIterator();
24334 	this.ignorePunctuation = typeof(ignorePunctuation) === "boolean" && ignorePunctuation;
24335 };
24336 
24337 /**
24338  * Return the first num code points in the source without advancing the
24339  * source pointer. If there are not enough code points left in the
24340  * string to satisfy the request, this method will return undefined. 
24341  * 
24342  * @param {number} num the number of characters to peek ahead
24343  * @return {string|undefined} a string formed out of up to num code points from
24344  * the start of the string, or undefined if there are not enough character left
24345  * in the source to complete the request
24346  */
24347 CodePointSource.prototype.peek = function(num) {
24348 	if (num < 1) {
24349 		return undefined;
24350 	}
24351 	if (this.chars.length < num && this.it.hasNext()) {
24352 		for (var i = 0; this.chars.length < 4 && this.it.hasNext(); i++) {
24353 			var c = this.it.next();
24354 			if (c && !this.ignorePunctuation || !isPunct(c)) {
24355 				this.chars.push(c);
24356 			}
24357 		}
24358 	}
24359 	if (this.chars.length < num) {
24360 		return undefined;
24361 	}
24362 	return this.chars.slice(0, num).join("");
24363 };
24364 /**
24365  * Advance the source pointer by the given number of code points.
24366  * @param {number} num number of code points to advance
24367  */
24368 CodePointSource.prototype.consume = function(num) {
24369 	if (num > 0) {
24370 		this.peek(num); // for the iterator to go forward if needed
24371 		if (num < this.chars.length) {
24372 			this.chars = this.chars.slice(num);
24373 		} else {
24374 			this.chars = [];
24375 		}
24376 	}
24377 };
24378 
24379 
24380 
24381 /*< ElementIterator.js */
24382 /*
24383  * ElementIterator.js - Iterate through a list of collation elements
24384  * 
24385  * Copyright © 2013-2015, JEDLSoft
24386  *
24387  * Licensed under the Apache License, Version 2.0 (the "License");
24388  * you may not use this file except in compliance with the License.
24389  * You may obtain a copy of the License at
24390  *
24391  *     http://www.apache.org/licenses/LICENSE-2.0
24392  *
24393  * Unless required by applicable law or agreed to in writing, software
24394  * distributed under the License is distributed on an "AS IS" BASIS,
24395  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24396  *
24397  * See the License for the specific language governing permissions and
24398  * limitations under the License.
24399  */
24400 
24401 /**
24402  * @class
24403  * An iterator through a sequence of collation elements. This
24404  * iterator takes a source of code points, converts them into
24405  * collation elements, and allows the caller to get single
24406  * elements at a time.
24407  * 
24408  * @constructor
24409  * @private
24410  * @param {CodePointSource} source source of code points to 
24411  * convert to collation elements
24412  * @param {Object} map mapping from sequences of code points to
24413  * collation elements
24414  * @param {number} keysize size in bits of the collation elements
24415  */
24416 var ElementIterator = function (source, map, keysize) {
24417 	this.elements = [];
24418 	this.source = source;
24419 	this.map = map;
24420 	this.keysize = keysize;
24421 };
24422 
24423 /**
24424  * @private
24425  */
24426 ElementIterator.prototype._fillBuffer = function () {
24427 	var str = undefined;
24428 	
24429 	// peek ahead by up to 4 characters, which may combine
24430 	// into 1 or more collation elements
24431 	for (var i = 4; i > 0; i--) {
24432 		str = this.source.peek(i);
24433 		if (str && this.map[str]) {
24434 			this.elements = this.elements.concat(this.map[str]);
24435 			this.source.consume(i);
24436 			return;
24437 		}
24438 	}
24439 	
24440 	if (str) {
24441 		// no mappings for the first code point, so just use its
24442 		// Unicode code point as a proxy for its sort order. Shift
24443 		// it by the key size so that everything unknown sorts
24444 		// after things that have mappings
24445 		this.elements.push(str.charCodeAt(0) << this.keysize);
24446 		this.source.consume(1);
24447 	} else {
24448 		// end of the string
24449 		return undefined;
24450 	}
24451 };
24452 
24453 /**
24454  * Return true if there are more collation elements left to
24455  * iterate through.
24456  * @returns {boolean} true if there are more elements left to
24457  * iterate through, and false otherwise
24458  */
24459 ElementIterator.prototype.hasNext = function () {
24460 	if (this.elements.length < 1) {
24461 		this._fillBuffer();
24462 	}
24463 	return !!this.elements.length;
24464 };
24465 
24466 /**
24467  * Return the next collation element. If more than one collation 
24468  * element is generated from a sequence of code points 
24469  * (ie. an "expansion"), then this class will buffer the
24470  * other elements and return them on subsequent calls to 
24471  * this method.
24472  * 
24473  * @returns {number|undefined} the next collation element or
24474  * undefined for no more collation elements
24475  */
24476 ElementIterator.prototype.next = function () {
24477 	if (this.elements.length < 1) {
24478 		this._fillBuffer();
24479 	}
24480 	var ret = this.elements[0];
24481 	this.elements = this.elements.slice(1);
24482 	return ret;
24483 };
24484 
24485 
24486 
24487 /*< Collator.js */
24488 /*
24489  * Collator.js - Collation routines
24490  * 
24491  * Copyright © 2013-2015, JEDLSoft
24492  *
24493  * Licensed under the Apache License, Version 2.0 (the "License");
24494  * you may not use this file except in compliance with the License.
24495  * You may obtain a copy of the License at
24496  *
24497  *     http://www.apache.org/licenses/LICENSE-2.0
24498  *
24499  * Unless required by applicable law or agreed to in writing, software
24500  * distributed under the License is distributed on an "AS IS" BASIS,
24501  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24502  *
24503  * See the License for the specific language governing permissions and
24504  * limitations under the License.
24505  */
24506 
24507 /* !depends 
24508 Locale.js
24509 ilib.js
24510 INumber.js
24511 isPunct.js
24512 NormString.js
24513 Utils.js
24514 JSUtils.js
24515 CodePointSource.js
24516 ElementIterator.js
24517 GlyphString.js
24518 */
24519 
24520 // !data collation
24521 
24522 
24523 /**
24524  * @class
24525  * A class that implements a locale-sensitive comparator function 
24526  * for use with sorting function. The comparator function
24527  * assumes that the strings it is comparing contain Unicode characters
24528  * encoded in UTF-16.<p>
24529  * 
24530  * Collations usually depend only on the language, because most collation orders 
24531  * are shared between locales that speak the same language. There are, however, a
24532  * number of instances where a locale collates differently than other locales
24533  * that share the same language. There are also a number of instances where a
24534  * locale collates differently based on the script used. This object can handle
24535  * these cases automatically if a full locale is specified in the options rather
24536  * than just a language code.<p>
24537  * 
24538  * <h2>Options</h2>
24539  * 
24540  * The options parameter can contain any of the following properties:
24541  * 
24542  * <ul>
24543  * <li><i>locale</i> - String|Locale. The locale which the comparator function 
24544  * will collate with. Default: the current iLib locale.
24545  * 
24546  * <li><i>sensitivity</i> - String. Sensitivity or strength of collator. This is one of 
24547  * "primary", "base", "secondary", "accent", "tertiary", "case", "quaternary", or 
24548  * "variant". Default: "primary"
24549  *   <ol>
24550  *   <li>base or primary - Only the primary distinctions between characters are significant.
24551  *   Another way of saying that is that the collator will be case-, accent-, and 
24552  *   variation-insensitive, and only distinguish between the base characters
24553  *   <li>case or secondary - Both the primary and secondary distinctions between characters
24554  *   are significant. That is, the collator will be accent- and variation-insensitive
24555  *   and will distinguish between base characters and character case.
24556  *   <li>accent or tertiary - The primary, secondary, and tertiary distinctions between
24557  *   characters are all significant. That is, the collator will be 
24558  *   variation-insensitive, but accent-, case-, and base-character-sensitive. 
24559  *   <li>variant or quaternary - All distinctions between characters are significant. That is,
24560  *   the algorithm is base character-, case-, accent-, and variation-sensitive.
24561  *   </ol>
24562  *   
24563  * <li><i>upperFirst</i> - boolean. When collating case-sensitively in a script that
24564  * has the concept of case, put upper-case
24565  * characters first, otherwise lower-case will come first. Warning: some browsers do
24566  * not implement this feature or at least do not implement it properly, so if you are 
24567  * using the native collator with this option, you may get different results in different
24568  * browsers. To guarantee the same results, set useNative to false to use the ilib 
24569  * collator implementation. This of course will be somewhat slower, but more 
24570  * predictable. Default: true
24571  * 
24572  * <li><i>reverse</i> - boolean. Return the list sorted in reverse order. When the
24573  * upperFirst option is also set to true, upper-case characters would then come at 
24574  * the end of the list. Default: false.
24575  * 
24576  * <li><i>scriptOrder</i> - string. When collating strings in multiple scripts,
24577  * this property specifies what order those scripts should be sorted. The default
24578  * Unicode Collation Algorithm (UCA) already has a default order for scripts, but
24579  * this can be tailored via this property. The value of this option is a 
24580  * space-separated list of ISO 15924 scripts codes. If a code is specified in this
24581  * property, its default data must be included using the JS assembly tool. If the
24582  * data is not included, the ordering for the script will be ignored. Default:
24583  * the default order defined by the UCA. 
24584  * 
24585  * <li><i>style</i> - The value of the style parameter is dependent on the locale.
24586  * For some locales, there are different styles of collating strings depending
24587  * on what kind of strings are being collated or what the preference of the user 
24588  * is. For example, in German, there is a phonebook order and a dictionary ordering
24589  * that sort the same array of strings slightly differently.
24590  * The static method {@link Collator#getAvailableStyles} will return a list of styles that ilib
24591  * currently knows about for any given locale. If the value of the style option is 
24592  * not recognized for a locale, it will be ignored. Default style is "standard".<p>
24593  * 
24594  * <li><i>usage</i> - Whether this collator will be used for searching or sorting.
24595  * Valid values are simply the strings "sort" or "search". When used for sorting,
24596  * it is good idea if a collator produces a stable sort. That is, the order of the 
24597  * sorted array of strings should not depend on the order of the strings in the
24598  * input array. As such, when a collator is supposed to act case insensitively, 
24599  * it nonetheless still distinguishes between case after all other criteria
24600  * are satisfied so that strings that are distinguished only by case do not sort
24601  * randomly. For searching, we would like to match two strings that different only 
24602  * by case, so the collator must return equals in that situation instead of 
24603  * further distinguishing by case. Default is "sort".
24604  * 
24605  * <li><i>numeric</i> - Treat the left and right strings as if they started with
24606  * numbers and sort them numerically rather than lexically.
24607  * 
24608  * <li><i>ignorePunctuation</i> - Skip punctuation characters when comparing the
24609  * strings.
24610  *  
24611  * <li>onLoad - a callback function to call when the collator object is fully 
24612  * loaded. When the onLoad option is given, the collator object will attempt to
24613  * load any missing locale data using the ilib loader callback.
24614  * When the constructor is done (even if the data is already preassembled), the 
24615  * onLoad function is called with the current instance as a parameter, so this
24616  * callback can be used with preassembled or dynamic loading or a mix of the two.
24617  * 
24618  * <li>sync - tell whether to load any missing locale data synchronously or 
24619  * asynchronously. If this option is given as "false", then the "onLoad"
24620  * callback must be given, as the instance returned from this constructor will
24621  * not be usable for a while. 
24622  *
24623  * <li><i>loadParams</i> - an object containing parameters to pass to the 
24624  * loader callback function when locale data is missing. The parameters are not
24625  * interpretted or modified in any way. They are simply passed along. The object 
24626  * may contain any property/value pairs as long as the calling code is in
24627  * agreement with the loader callback function as to what those parameters mean.
24628  * 
24629  * <li><i>useNative</i> - when this option is true, use the native Intl object
24630  * provided by the Javascript engine, if it exists, to implement this class. If
24631  * it doesn't exist, or if this parameter is false, then this class uses a pure 
24632  * Javascript implementation, which is slower and uses a lot more memory, but 
24633  * works everywhere that ilib works. Default is "true".
24634  * </ul>
24635  * 
24636  * <h2>Operation</h2>
24637  * 
24638  * The Collator constructor returns a collator object tailored with the above 
24639  * options. The object contains an internal compare() method which compares two 
24640  * strings according to those options. This can be used directly to compare
24641  * two strings, but is not useful for passing to the javascript sort function
24642  * because then it will not have its collation data available. Instead, use the 
24643  * getComparator() method to retrieve a function that is bound to the collator
24644  * object. (You could also bind it yourself using ilib.bind()). The bound function 
24645  * can be used with the standard Javascript array sorting algorithm, or as a 
24646  * comparator with your own sorting algorithm.<p>
24647  * 
24648  * Example using the standard Javascript array sorting call with the bound
24649  * function:<p>
24650  * 
24651  * <code>
24652  * <pre>
24653  * var arr = ["ö", "oe", "ü", "o", "a", "ae", "u", "ß", "ä"];
24654  * var collator = new Collator({locale: 'de-DE', style: "dictionary"});
24655  * arr.sort(collator.getComparator());
24656  * console.log(JSON.stringify(arr));
24657  * </pre>
24658  * </code>
24659  * <p>
24660  * 
24661  * Would give the output:<p>
24662  * 
24663  * <code>
24664  * <pre>
24665  * ["a", "ae", "ä", "o", "oe", "ö", "ß", "u", "ü"]
24666  * </pre>
24667  * </code>
24668  * 
24669  * When sorting an array of Javascript objects according to one of the 
24670  * string properties of the objects, wrap the collator's compare function 
24671  * in your own comparator function that knows the structure of the objects
24672  * being sorted:<p>
24673  * 
24674  * <code>
24675  * <pre>
24676  * var collator = new Collator({locale: 'de-DE'});
24677  * var myComparator = function (collator) {
24678  *   var comparator = collator.getComparator();
24679  *   // left and right are your own objects
24680  *   return function (left, right) {
24681  *   	return comparator(left.x.y.textProperty, right.x.y.textProperty);
24682  *   };
24683  * };
24684  * arr.sort(myComparator(collator));
24685  * </pre>
24686  * </code>
24687  * <p>
24688  * 
24689  * <h2>Sort Keys</h2>
24690  * 
24691  * The collator class also has a method to retrieve the sort key for a
24692  * string. The sort key is an array of values that represent how each  
24693  * character in the string should be collated according to the characteristics
24694  * of the collation algorithm and the given options. Thus, sort keys can be 
24695  * compared directly value-for-value with other sort keys that were generated 
24696  * by the same collator, and the resulting ordering is guaranteed to be the 
24697  * same as if the original strings were compared by the collator.
24698  * Sort keys generated by different collators are not guaranteed to give
24699  * any reasonable results when compared together unless the two collators 
24700  * were constructed with 
24701  * exactly the same options and therefore end up representing the exact same 
24702  * collation sequence.<p>
24703  * 
24704  * A good rule of thumb is that you would use a sort key if you had 10 or more
24705  * items to sort or if your array might be resorted arbitrarily. For example, if your 
24706  * user interface was displaying a table with 100 rows in it, and each row had
24707  * 4 sortable text columns which could be sorted in acending or descending order,
24708  * the recommended practice would be to generate a sort key for each of the 4
24709  * sortable fields in each row and store that in the Javascript representation of the
24710  * table data. Then, when the user clicks on a column header to resort the
24711  * table according to that column, the resorting would be relatively quick 
24712  * because it would only be comparing arrays of values, and not recalculating 
24713  * the collation values for each character in each string for every comparison.<p>
24714  * 
24715  * For tables that are large, it is usually a better idea to do the sorting
24716  * on the server side, especially if the table is the result of a database
24717  * query. In this case, the table is usually a view of the cursor of a large
24718  * results set, and only a few entries are sent to the front end at a time.
24719  * In order to sort the set efficiently, it should be done on the database
24720  * level instead.
24721  * 
24722  * <h2>Data</h2>
24723  * 
24724  * Doing correct collation entails a huge amount of mapping data, much of which is
24725  * not necessary when collating in one language with one script, which is the most
24726  * common case. Thus, ilib implements a number of ways to include the data you
24727  * need or leave out the data you don't need using the JS assembly tool:
24728  * 
24729  * <ol>
24730  * <li>Full multilingual data - if you are sorting multilingual data and need to collate 
24731  * text written in multiple scripts, you can use the directive "!data collation/ducet" to 
24732  * load in the full collation data.  This allows the collator to perform the entire 
24733  * Unicode Collation Algorithm (UCA) based on the Default Unicode Collation Element 
24734  * Table (DUCET). The data is very large, on the order of multiple megabytes, but 
24735  * sometimes it is necessary.
24736  * <li>A few scripts - if you are sorting text written in only a few scripts, you may 
24737  * want to include only the data for those scripts. Each ISO 15924 script code has its
24738  * own data available in a separate file, so you can use the data directive to include
24739  * only the data for the scripts you need. For example, use  
24740  * "!data collation/Latn" to retrieve the collation information for the Latin script.
24741  * Because the "ducet" table mentioned in the previous point is a superset of the 
24742  * tables for all other scripts, you do not need to include explicitly the data for 
24743  * any particular script when using "ducet". That is, you either include "ducet" or 
24744  * you include a specific list of scripts.
24745  * <li>Only one script - if you are sorting text written only in one script, you can
24746  * either include the data directly as in the previous point, or you can rely on the 
24747  * locale to include the correct data for you. In this case, you can use the directive
24748  * "!data collate" to load in the locale's collation data for its most common script.
24749  * </ol>
24750  *   
24751  * With any of the above ways of including the data, the collator will only perform the
24752  * correct language-sensitive sorting for the given locale. All other scripts will be
24753  * sorted in the default manner according to the UCA. For example, if you include the
24754  * "ducet" data and pass in "de-DE" (German for Germany) as the locale spec, then
24755  * only the Latin script (the default script for German) will be sorted according to
24756  * German rules. All other scripts in the DUCET, such as Japanese or Arabic, will use 
24757  * the default UCA collation rules.<p>
24758  * 
24759  * If this collator encounters a character for which it has no collation data, it will
24760  * sort those characters by pure Unicode value after all characters for which it does have
24761  * collation data. For example, if you only loaded in the German collation data (ie. the
24762  * data for the Latin script tailored to German) to sort a list of person names, but that
24763  * list happens to include the names of a few Japanese people written in Japanese 
24764  * characters, the Japanese names will sort at the end of the list after all German names,
24765  * and will sort according to the Unicode values of the characters.
24766  * 
24767  * @constructor
24768  * @param {Object} options options governing how the resulting comparator 
24769  * function will operate
24770  */
24771 var Collator = function(options) {
24772 	var sync = true,
24773 		loadParams = undefined,
24774 		useNative = true;
24775 
24776 	// defaults
24777 	/** 
24778 	 * @private
24779 	 * @type {Locale} 
24780 	 */
24781 	this.locale = new Locale(ilib.getLocale());
24782 	
24783 	/** @private */
24784 	this.caseFirst = "upper";
24785 	/** @private */
24786 	this.sensitivity = "variant";
24787 	/** @private */
24788 	this.level = 4;
24789 	/** @private */
24790 	this.usage = "sort";
24791 	/** @private */
24792 	this.reverse = false;
24793 	/** @private */
24794 	this.numeric = false;
24795 	/** @private */
24796 	this.style = "default";
24797 	/** @private */
24798 	this.ignorePunctuation = false;
24799 	
24800 	if (options) {
24801 		if (options.locale) {
24802 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
24803 		}
24804 		if (options.sensitivity) {
24805 			switch (options.sensitivity) {
24806 				case 'primary':
24807 				case 'base':
24808 					this.sensitivity = "base";
24809 					this.level = 1;
24810 					break;
24811 				case 'secondary':
24812 				case 'accent':
24813 					this.sensitivity = "accent";
24814 					this.level = 2;
24815 					break;
24816 				case 'tertiary':
24817 				case 'case':
24818 					this.sensitivity = "case";
24819 					this.level = 3;
24820 					break;
24821 				case 'quaternary':
24822 				case 'variant':
24823 					this.sensitivity = "variant";
24824 					this.level = 4;
24825 					break;
24826 			}
24827 		}
24828 		if (typeof(options.upperFirst) !== 'undefined') {
24829 			this.caseFirst = options.upperFirst ? "upper" : "lower"; 
24830 		}
24831 		
24832 		if (typeof(options.ignorePunctuation) !== 'undefined') {
24833 			this.ignorePunctuation = options.ignorePunctuation;
24834 		}
24835 		if (typeof(options.sync) !== 'undefined') {
24836 			sync = !!options.sync;
24837 		}
24838 		
24839 		loadParams = options.loadParams;
24840 		if (typeof(options.useNative) !== 'undefined') {
24841 			useNative = options.useNative;
24842 		}
24843 		
24844 		if (options.usage === "sort" || options.usage === "search") {
24845 			this.usage = options.usage;
24846 		}
24847 		
24848 		if (typeof(options.reverse) === 'boolean') {
24849 			this.reverse = options.reverse;
24850 		}
24851 
24852 		if (typeof(options.numeric) === 'boolean') {
24853 			this.numeric = options.numeric;
24854 		}
24855 		
24856 		if (typeof(options.style) === 'string') {
24857 			this.style = options.style;
24858 		}
24859 	} else {
24860 	    options = {sync: true};
24861 	}
24862 
24863 	if (this.usage === "sort") {
24864 		// produces a stable sort
24865 		this.level = 4;
24866 	}
24867 
24868 	if (useNative && typeof(Intl) !== 'undefined' && Intl) {
24869 		// this engine is modern and supports the new Intl object!
24870 		//console.log("implemented natively");
24871 		/** 
24872 		 * @private
24873 		 * @type {{compare:function(string,string)}} 
24874 		 */
24875 		this.collator = new Intl.Collator(this.locale.getSpec(), {
24876 			sensitivity: this.sensitivity,
24877 			caseFirst: this.caseFirst,
24878 			ignorePunctuation: this.ignorePunctuation,
24879 			numeric: this.numeric,
24880 			usage: this.usage
24881 		});
24882 		
24883 		if (options && typeof(options.onLoad) === 'function') {
24884 			options.onLoad(this);
24885 		}
24886 	} else {
24887 		//console.log("implemented in pure JS");
24888 		
24889 		// else implement in pure Javascript
24890 		Utils.loadData({
24891 			object: "Collator", 
24892 			locale: this.locale, 
24893 			name: "collation.json",
24894 			sync: sync,
24895 			loadParams: loadParams, 
24896 			callback: ilib.bind(this, function (collation) {
24897 				if (!collation) {
24898 					collation = ilib.data.collation;
24899 					var spec = this.locale.getSpec().replace(/-/g, '_');
24900 					ilib.data.cache.Collator[spec] = collation;
24901 				}
24902 				this._initCollation(collation);
24903 		        if (this.ignorePunctuation) {
24904 		            isPunct._init(sync, loadParams, ilib.bind(this, function() {
24905 		                this._init(options);
24906 		            }));
24907 		        } else {
24908 		            this._init(options);
24909 		        }
24910 			})
24911 		});
24912 	}
24913 };
24914 
24915 Collator.prototype = {
24916     /**
24917      * @private
24918      */
24919     _init: function(options) {
24920         if (this.numeric) {
24921             // Create a fake INumber instance now to guarantee that the locale data 
24922             // is loaded so we can create sync INumber instances later, even in async mode
24923             new INumber("1", {
24924                 sync: options.sync,
24925                 loadParams: options.loadParams,
24926                 onLoad: function(n) {
24927                     if (typeof(options.onLoad) === 'function') {
24928                         options.onLoad(this);
24929                     }
24930                 }
24931             })
24932         } else {
24933             if (typeof(options.onLoad) === 'function') {
24934                 options.onLoad(this);
24935             }
24936         }
24937     },
24938         
24939 	/**
24940 	 * @private
24941 	 * Bit pack an array of values into a single number
24942 	 * @param {number|null|Array.<number>} arr array of values to bit pack
24943 	 * @param {number} offset offset for the start of this map
24944 	 */
24945 	_pack: function (arr, offset) {
24946 		var value = 0;
24947 		if (arr) {
24948 			if (typeof(arr) === 'number') {
24949 				arr = [ arr ];
24950 			}
24951 			for (var i = 0; i < this.level; i++) {
24952 				var thisLevel = (typeof(arr[i]) !== "undefined" ? arr[i] : 0);
24953 				if (i === 0) {
24954 					thisLevel += offset;
24955 				}
24956 				if (i > 0) {
24957 					value <<= this.collation.bits[i];	
24958 				}
24959 				if (i === 2 && this.caseFirst === "lower") {
24960 					// sort the lower case first instead of upper
24961 					value = value | (1 - thisLevel);
24962 				} else {
24963 					value = value | thisLevel;
24964 				}
24965 			}
24966 		}
24967 		return value;
24968 	},
24969 	
24970 	/**
24971 	 * @private
24972 	 * Return the rule packed into an array of collation elements.
24973 	 * @param {Array.<number|null|Array.<number>>} rule
24974 	 * @param {number} offset
24975 	 * @return {Array.<number>} a bit-packed array of numbers
24976 	 */
24977 	_packRule: function(rule, offset) {
24978 		if (ilib.isArray(rule[0])) {
24979 			var ret = [];
24980 			for (var i = 0; i < rule.length; i++) {
24981 				ret.push(this._pack(rule[i], offset));
24982 			}
24983 			return ret;
24984 		} else {
24985 			return [ this._pack(rule, offset) ];
24986 		}
24987 	},
24988     
24989 	/**
24990 	 * @private
24991 	 */
24992 	_addChars: function (str, offset) {
24993 		var gs = new GlyphString(str);
24994 		var it = gs.charIterator();
24995 		var c;
24996 		
24997 		while (it.hasNext()) {
24998 			c = it.next();
24999 			if (c === "'") {
25000 				// escape a sequence of chars as one collation element
25001 				c = "";
25002 				var x = "";
25003 				while (it.hasNext() && x !== "'") {
25004 					c += x;
25005 					x = it.next();
25006 				}
25007 			}
25008 			this.lastMap++;
25009 			this.map[c] = this._packRule([this.lastMap], offset);
25010 		}
25011 	},
25012 	
25013 	/**
25014 	 * @private
25015 	 */
25016 	_addRules: function(rules, start) {
25017 	    var p;
25018 	    for (var r in rules.map) {
25019 	        if (r) {
25020 	            this.map[r] = this._packRule(rules.map[r], start);
25021 	            p = typeof(rules.map[r][0]) === 'number' ? rules.map[r][0] : rules.map[r][0][0];
25022 	            this.lastMap = Math.max(p + start, this.lastMap);
25023 	        }
25024 	    }
25025 
25026 	    if (typeof(rules.ranges) !== 'undefined') {
25027 	        // for each range, everything in the range goes in primary sequence from the start
25028 	        for (var i = 0; i < rules.ranges.length; i++) {
25029 	            var range = rules.ranges[i];
25030 
25031 	            this.lastMap = range.start;
25032 	            if (typeof(range.chars) === "string") {
25033 	                this._addChars(range.chars, start);
25034 	            } else {
25035 	                for (var k = 0; k < range.chars.length; k++) {
25036 	                    this._addChars(range.chars[k], start);
25037 	                }
25038 	            }
25039 	        }
25040 	    }
25041 	},
25042 	
25043 	/**
25044      * @private
25045      */
25046 	_initCollation: function(rules) {
25047 	    var rule = this.style;
25048 	    while (typeof(rule) === 'string') {
25049 	        rule = rules[rule];
25050 	    }
25051 
25052 	    if (!rule) {
25053 	        rule = "default";
25054 
25055 	        while (typeof(rule) === 'string') {
25056 	            rule = rules[rule];
25057 	        }
25058 	    }
25059 	    if (!rule) {
25060 	        this.map = {};
25061 	        return;
25062 	    }
25063 
25064 	    /** 
25065 	     * @private
25066 	     * @type {{scripts:Array.<string>,bits:Array.<number>,maxes:Array.<number>,bases:Array.<number>,map:Object.<string,Array.<number|null|Array.<number>>>}}
25067 	     */
25068 	    this.collation = rule;
25069 	    this.map = {};
25070 	    this.lastMap = -1;
25071 	    this.keysize = this.collation.keysize[this.level-1];
25072 	    this.defaultRule = rules["default"];
25073 
25074 	    if (typeof(this.collation.inherit) !== 'undefined') {
25075 	        for (var i = 0; i < this.collation.inherit.length; i++) {
25076 	            if (this.collation.inherit === 'this') {
25077 	                continue;
25078 	            }
25079 	            var col = this.collation.inherit[i];
25080 	            rule = typeof(col) === 'object' ? col.name : col;
25081 	            if (rules[rule]) {
25082 	                this._addRules(rules[rule], col.start || this.lastMap+1);
25083 	            }
25084 	        }
25085 	    }
25086 	    this._addRules(this.collation, this.lastMap+1);
25087 	},
25088     
25089     /**
25090      * @private
25091      */
25092     _basicCompare: function(left, right) {
25093 		var l = (left instanceof NormString) ? left : new NormString(left),
25094 			r = (right instanceof NormString) ? right : new NormString(right),
25095 			lelements,
25096 			relements,
25097 			diff;
25098 		
25099 		if (this.numeric) {
25100 			var lvalue = new INumber(left, {locale: this.locale});
25101 			var rvalue = new INumber(right, {locale: this.locale});
25102 			if (!isNaN(lvalue.valueOf()) && !isNaN(rvalue.valueOf())) {
25103 				diff = lvalue.valueOf() - rvalue.valueOf();
25104 				if (diff) {
25105 					return diff;
25106 				} else {
25107 					// skip the numeric part and compare the rest lexically
25108 					l = new NormString(left.substring(lvalue.parsed.length));
25109 					r = new NormString(right.substring(rvalue.parsed.length));
25110 				}
25111 			}
25112 			// else if they aren't both numbers, then let the code below take care of the lexical comparison instead
25113 		}
25114 			
25115 		lelements = new ElementIterator(new CodePointSource(l, this.ignorePunctuation), this.map, this.keysize);
25116 		relements = new ElementIterator(new CodePointSource(r, this.ignorePunctuation), this.map, this.keysize);
25117 		
25118 		while (lelements.hasNext() && relements.hasNext()) {
25119 			diff = lelements.next() - relements.next();
25120 			if (diff) {
25121 				return diff;
25122 			}
25123 		}
25124 		if (!lelements.hasNext() && !relements.hasNext()) {
25125 			return 0;
25126 		} else if (lelements.hasNext()) {
25127 			return 1;
25128 		} else {
25129 			return -1;
25130 		}
25131     },
25132     
25133 	/**
25134 	 * Compare two strings together according to the rules of this 
25135 	 * collator instance. Do not use this function directly with 
25136 	 * Array.sort, as it will not have its collation data available
25137 	 * and therefore will not function properly. Use the function
25138 	 * returned by getComparator() instead.
25139 	 * 
25140 	 * @param {string} left the left string to compare
25141 	 * @param {string} right the right string to compare
25142 	 * @return {number} a negative number if left comes before right, a
25143 	 * positive number if right comes before left, and zero if left and 
25144 	 * right are equivalent according to this collator
25145 	 */
25146 	compare: function (left, right) {
25147 		// last resort: use the "C" locale
25148 		if (this.collator) {
25149 			// implemented by the core engine
25150 			return this.collator.compare(left, right);
25151 		}
25152 
25153 		var ret = this._basicCompare(left, right);
25154 		return this.reverse ? -ret : ret;
25155 	},
25156 	
25157 	/**
25158 	 * Return a comparator function that can compare two strings together
25159 	 * according to the rules of this collator instance. The function 
25160 	 * returns a negative number if the left 
25161 	 * string comes before right, a positive number if the right string comes 
25162 	 * before the left, and zero if left and right are equivalent. If the
25163 	 * reverse property was given as true to the collator constructor, this 
25164 	 * function will
25165 	 * switch the sign of those values to cause sorting to happen in the
25166 	 * reverse order.
25167 	 * 
25168 	 * @return {function(...)|undefined} a comparator function that 
25169 	 * can compare two strings together according to the rules of this 
25170 	 * collator instance
25171 	 */
25172 	getComparator: function() {
25173 		// bind the function to this instance so that we have the collation
25174 		// rules available to do the work
25175 		if (this.collator) {
25176 			// implemented by the core engine
25177 			return this.collator.compare;
25178 		}
25179 		
25180 		return ilib.bind(this, this.compare);
25181 	},
25182 	
25183 	/**
25184 	 * Return a sort key string for the given string. The sort key
25185 	 * string is a list of values that represent each character 
25186 	 * in the original string. The sort key
25187 	 * values for any particular character consists of 3 numbers that
25188 	 * encode the primary, secondary, and tertiary characteristics
25189 	 * of that character. The values of each characteristic are 
25190 	 * modified according to the strength of this collator instance 
25191 	 * to give the correct collation order. The idea is that this
25192 	 * sort key string is directly comparable byte-for-byte to 
25193 	 * other sort key strings generated by this collator without
25194 	 * any further knowledge of the collation rules for the locale.
25195 	 * More formally, if a < b according to the rules of this collation, 
25196 	 * then it is guaranteed that sortkey(a) < sortkey(b) when compared
25197 	 * byte-for-byte. The sort key string can therefore be used
25198 	 * without the collator to sort an array of strings efficiently
25199 	 * because the work of determining the applicability of various
25200 	 * collation rules is done once up-front when generating 
25201 	 * the sort key.<p>
25202 	 * 
25203 	 * The sort key string can be treated as a regular, albeit somewhat
25204 	 * odd-looking, string. That is, it can be pass to regular 
25205 	 * Javascript functions without problems.  
25206 	 * 
25207 	 * @param {string} str the original string to generate the sort key for
25208 	 * @return {string} a sort key string for the given string
25209 	 */
25210 	sortKey: function (str) {
25211 		if (!str) {
25212 			return "";
25213 		}
25214 		
25215 		if (this.collator) {
25216 			// native, no sort keys available
25217 			return str;
25218 		}
25219 		
25220 		if (this.numeric) {
25221 			var v = new INumber(str, {locale: this.locale});
25222 			var s = isNaN(v.valueOf()) ? "" : v.valueOf().toString(16);
25223 			return JSUtils.pad(s, 16);	
25224 		} else {
25225 			var n = (typeof(str) === "string") ? new NormString(str) : str,
25226 				ret = "",
25227 				lelements = new ElementIterator(new CodePointSource(n, this.ignorePunctuation), this.map, this.keysize),
25228 				element;
25229 			
25230 			while (lelements.hasNext()) {
25231 				element = lelements.next();
25232 				if (this.reverse) {
25233 					// for reverse, take the bitwise inverse
25234 					element = (1 << this.keysize) - element;
25235 				}
25236 				ret += JSUtils.pad(element.toString(16), this.keysize/4);	
25237 			}
25238 		}
25239 		return ret;
25240 	}
25241 };
25242 
25243 /**
25244  * Retrieve the list of collation style names that are available for the 
25245  * given locale. This list varies depending on the locale, and depending
25246  * on whether or not the data for that locale was assembled into this copy
25247  * of ilib.
25248  * 
25249  * @param {Locale|string=} locale The locale for which the available
25250  * styles are being sought
25251  * @return Array.<string> an array of style names that are available for
25252  * the given locale
25253  */
25254 Collator.getAvailableStyles = function (locale) {
25255 	return [ "standard" ];
25256 };
25257 
25258 /**
25259  * Retrieve the list of ISO 15924 script codes that are available in this
25260  * copy of ilib. This list varies depending on whether or not the data for 
25261  * various scripts was assembled into this copy of ilib. If the "ducet"
25262  * data is assembled into this copy of ilib, this method will report the
25263  * entire list of scripts as being available. If a collator instance is
25264  * instantiated with a script code that is not on the list returned by this
25265  * function, it will be ignored and text in that script will be sorted by
25266  * numeric Unicode values of the characters.
25267  * 
25268  * @return Array.<string> an array of ISO 15924 script codes that are 
25269  * available
25270  */
25271 Collator.getAvailableScripts = function () {
25272 	return [ "Latn" ];
25273 };
25274 
25275 
25276 /**
25277  * Return a default collation style
25278  *  
25279  * @returns {string} default collation style such as 'latin', 'korean' etc */
25280 Collator.prototype.getDefaultCollatorStyle = function () {
25281 	return this.defaultRule;
25282 };
25283 
25284 
25285 
25286 /*< nfd/all.js */
25287 /*
25288  * all.js - include file for normalization data for a particular script
25289  * 
25290  * Copyright © 2013-2015, JEDLSoft
25291  *
25292  * Licensed under the Apache License, Version 2.0 (the "License");
25293  * you may not use this file except in compliance with the License.
25294  * You may obtain a copy of the License at
25295  *
25296  *     http://www.apache.org/licenses/LICENSE-2.0
25297  *
25298  * Unless required by applicable law or agreed to in writing, software
25299  * distributed under the License is distributed on an "AS IS" BASIS,
25300  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25301  *
25302  * See the License for the specific language governing permissions and
25303  * limitations under the License.
25304  */
25305 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
25306 // !data normdata nfd/all
25307 ilib.extend(ilib.data.norm, ilib.data.normdata);
25308 ilib.extend(ilib.data.norm.nfd, ilib.data.nfd_all);
25309 ilib.data.normdata = undefined;
25310 ilib.data.nfd_all = undefined;
25311 /*< nfkd/all.js */
25312 /*
25313  * all.js - include file for normalization data for a particular script
25314  * 
25315  * Copyright © 2013-2015, JEDLSoft
25316  *
25317  * Licensed under the Apache License, Version 2.0 (the "License");
25318  * you may not use this file except in compliance with the License.
25319  * You may obtain a copy of the License at
25320  *
25321  *     http://www.apache.org/licenses/LICENSE-2.0
25322  *
25323  * Unless required by applicable law or agreed to in writing, software
25324  * distributed under the License is distributed on an "AS IS" BASIS,
25325  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25326  *
25327  * See the License for the specific language governing permissions and
25328  * limitations under the License.
25329  */
25330 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
25331 // !depends nfd/all.js
25332 // !data normdata nfkd/all
25333 ilib.extend(ilib.data.norm, ilib.data.normdata);
25334 ilib.extend(ilib.data.norm.nfkd, ilib.data.nfkd_all);
25335 ilib.data.normdata = undefined;
25336 ilib.data.nfkd_all = undefined;
25337 /*< nfkc/all.js */
25338 /*
25339  * all.js - include file for normalization data for a particular script
25340  * 
25341  * Copyright © 2013-2015, JEDLSoft
25342  *
25343  * Licensed under the Apache License, Version 2.0 (the "License");
25344  * you may not use this file except in compliance with the License.
25345  * You may obtain a copy of the License at
25346  *
25347  *     http://www.apache.org/licenses/LICENSE-2.0
25348  *
25349  * Unless required by applicable law or agreed to in writing, software
25350  * distributed under the License is distributed on an "AS IS" BASIS,
25351  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25352  *
25353  * See the License for the specific language governing permissions and
25354  * limitations under the License.
25355  */
25356 /* WARNING: THIS IS A FILE GENERATED BY gennorm.js. DO NOT EDIT BY HAND. */
25357 // !depends nfd/all.js nfkd/all.js
25358 // !data norm
25359 ilib.extend(ilib.data.norm, ilib.data.normdata);
25360 ilib.data.normdata = undefined;
25361 
25362 /*< LocaleMatcher.js */
25363 /*
25364  * LocaleMatcher.js - Locale matcher definition
25365  *
25366  * Copyright © 2013-2015, JEDLSoft
25367  *
25368  * Licensed under the Apache License, Version 2.0 (the "License");
25369  * you may not use this file except in compliance with the License.
25370  * You may obtain a copy of the License at
25371  *
25372  *     http://www.apache.org/licenses/LICENSE-2.0
25373  *
25374  * Unless required by applicable law or agreed to in writing, software
25375  * distributed under the License is distributed on an "AS IS" BASIS,
25376  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25377  *
25378  * See the License for the specific language governing permissions and
25379  * limitations under the License.
25380  */
25381 
25382 // !depends ilib.js Locale.js Utils.js
25383 // !data localematch
25384 
25385 
25386 var componentWeights = [
25387 	0.5,   // language
25388 	0.2,   // script
25389 	0.25,  // region
25390 	0.05   // variant
25391 ];
25392 
25393 /**
25394  * @class
25395  * Create a new locale matcher instance. This is used
25396  * to see which locales can be matched with each other in
25397  * various ways.<p>
25398  *
25399  * The options object may contain any of the following properties:
25400  *
25401  * <ul>
25402  * <li><i>locale</i> - the locale instance or locale spec to match
25403  *
25404  * <li><i>onLoad</i> - a callback function to call when the locale matcher object is fully
25405  * loaded. When the onLoad option is given, the locale matcher object will attempt to
25406  * load any missing locale data using the ilib loader callback.
25407  * When the constructor is done (even if the data is already preassembled), the
25408  * onLoad function is called with the current instance as a parameter, so this
25409  * callback can be used with preassembled or dynamic loading or a mix of the two.
25410  *
25411  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
25412  * asynchronously. If this option is given as "false", then the "onLoad"
25413  * callback must be given, as the instance returned from this constructor will
25414  * not be usable for a while.
25415  *
25416  * <li><i>loadParams</i> - an object containing parameters to pass to the
25417  * loader callback function when locale data is missing. The parameters are not
25418  * interpretted or modified in any way. They are simply passed along. The object
25419  * may contain any property/value pairs as long as the calling code is in
25420  * agreement with the loader callback function as to what those parameters mean.
25421  * </ul>
25422  *
25423  *
25424  * @constructor
25425  * @param {Object} options parameters to initialize this matcher
25426  */
25427 var LocaleMatcher = function(options) {
25428 	var sync = true,
25429 	    loadParams = undefined;
25430 
25431 	this.locale = new Locale();
25432 
25433 	if (options) {
25434 		if (typeof(options.locale) !== 'undefined') {
25435 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
25436 		}
25437 
25438 		if (typeof(options.sync) !== 'undefined') {
25439 			sync = !!options.sync;
25440 		}
25441 
25442 		if (typeof(options.loadParams) !== 'undefined') {
25443 			loadParams = options.loadParams;
25444 		}
25445 	}
25446 
25447 	if (typeof(ilib.data.localematch) === 'undefined') {
25448 		Utils.loadData({
25449 			object: "LocaleMatcher",
25450 			locale: "-",
25451 			name: "localematch.json",
25452 			sync: sync,
25453 			loadParams: loadParams,
25454 			callback: ilib.bind(this, function (info) {
25455 				if (!info) {
25456 					info = {};
25457 					var spec = this.locale.getSpec().replace(/-/g, "_");
25458 					ilib.data.cache.LocaleMatcher[spec] = info;
25459 				}
25460 				/** @type {Object.<string,string>} */
25461 				this.info = info;
25462 				if (options && typeof(options.onLoad) === 'function') {
25463 					options.onLoad(this);
25464 				}
25465 			})
25466 		});
25467 	} else {
25468 		this.info = ilib.data.localematch;
25469 		if (options && typeof(options.onLoad) === 'function') {
25470             options.onLoad(this);
25471         }
25472 	}
25473 };
25474 
25475 
25476 LocaleMatcher.prototype = {
25477 	/**
25478 	 * Return the locale used to construct this instance.
25479 	 * @return {Locale|undefined} the locale for this matcher
25480 	 */
25481 	getLocale: function() {
25482 		return this.locale;
25483 	},
25484 
25485 	/**
25486 	 * @private
25487 	 * Do the work
25488 	 */
25489 	_getLikelyLocale: function(locale) {
25490 	    // already full specified
25491 	    if (locale.language && locale.script && locale.region) return locale;
25492 	    
25493         if (typeof(this.info.likelyLocales[locale.getSpec()]) === 'undefined') {
25494             // try various partials before giving up
25495             var partial = this.info.likelyLocales[new Locale(locale.language, undefined, locale.region).getSpec()];
25496             if (typeof(partial) !== 'undefined') return new Locale(partial);
25497 
25498             partial = this.info.likelyLocales[new Locale(locale.language, locale.script, undefined).getSpec()];
25499             if (typeof(partial) !== 'undefined') return new Locale(partial);
25500 
25501             partial = this.info.likelyLocales[new Locale(locale.language, undefined, undefined).getSpec()];
25502             if (typeof(partial) !== 'undefined') return new Locale(partial);
25503 
25504             partial = this.info.likelyLocales[new Locale(undefined, locale.script, locale.region).getSpec()];
25505             if (typeof(partial) !== 'undefined') return new Locale(partial);
25506 
25507             partial = this.info.likelyLocales[new Locale(undefined, undefined, locale.region).getSpec()];
25508             if (typeof(partial) !== 'undefined') return new Locale(partial);
25509 
25510             partial = this.info.likelyLocales[new Locale(undefined, locale.script, undefined).getSpec()];
25511             if (typeof(partial) !== 'undefined') return new Locale(partial);
25512 
25513             return locale;
25514         }
25515 
25516         return new Locale(this.info.likelyLocales[locale.getSpec()]);
25517 	},
25518 
25519 	/**
25520 	 * Return an Locale instance that is fully specified based on partial information
25521 	 * given to the constructor of this locale matcher instance. For example, if the locale
25522 	 * spec given to this locale matcher instance is simply "ru" (for the Russian language),
25523 	 * then it will fill in the missing region and script tags and return a locale with
25524 	 * the specifier "ru-Cyrl-RU". (ie. Russian language, Cyrillic, Russian Federation).
25525 	 * Any one or two of the language, script, or region parts may be left unspecified,
25526 	 * and the other one or two parts will be filled in automatically. If this
25527 	 * class has no information about the given locale, then the locale of this
25528 	 * locale matcher instance is returned unchanged.
25529 	 *
25530 	 * @returns {Locale} the most likely completion of the partial locale given
25531 	 * to the constructor of this locale matcher instance
25532 	 */
25533 	getLikelyLocale: function () {
25534 	    return this._getLikelyLocale(this.locale);
25535 	},
25536 
25537 	/**
25538 	 * Return the degree that the given locale matches the current locale of this
25539 	 * matcher. This method returns an integer from 0 to 100. A value of 100 is
25540 	 * a 100% match, meaning that the two locales are exactly equivalent to each
25541 	 * other. (eg. "ja-JP" and "ja-JP") A value of 0 means that there 0% match or
25542 	 * that the two locales have nothing in common. (eg. "en-US" and "ja-JP") <p>
25543 	 *
25544 	 * Locale matching is not the same as equivalence, as the degree of matching
25545 	 * is returned. (See Locale.equals for equivalence.)<p>
25546 	 *
25547 	 * The match score is calculated based on matching the 4 locale components,
25548 	 * weighted by importance:
25549 	 *
25550 	 * <ul>
25551 	 * <li> language - this accounts for 50% of the match score
25552 	 * <li> region - accounts for 25% of the match score
25553 	 * <li> script - accounts for 20% of the match score
25554 	 * <li> variant - accounts for 5% of the match score
25555 	 * </ul>
25556 	 *
25557 	 * The score is affected by the following things:
25558 	 *
25559 	 * <ul>
25560 	 * <li> A large language score is given when the language components of the locales
25561 	 * match exactly.
25562 	 * <li> Higher language scores are given when the languages are linguistically
25563 	 * close to each other, such as dialects.
25564 	 * <li> A small score is given when two languages are in the same
25565 	 * linguistic family, but one is not a dialect of the other, such as German
25566 	 * and Dutch.
25567 	 * <li> A large region score is given when two locales share the same region.
25568 	 * <li> A smaller region score is given when one region is contained within
25569 	 * another. For example, Hong Kong is part of China, so a moderate score is
25570 	 * given instead of a full score.
25571 	 * <li> A small score is given if two regions are geographically close to
25572 	 * each other or are tied by history. For example, Ireland and Great Britain
25573 	 * are both adjacent and tied by history, so they receive a moderate score.
25574 	 * <li> A high script score is given if the two locales share the same script.
25575 	 * The legibility of a common script means that there is some small kinship of the
25576 	 * different languages.
25577 	 * <li> A high variant score is given if the two locales share the same
25578 	 * variant. Full score is given when both locales have no variant at all.
25579 	 * <li> Locale components that are unspecified in both locales are given high
25580 	 * scores.
25581 	 * <li> Locales where a particular locale component is missing in only one
25582 	 * locale can still match when the default for that locale component matches
25583 	 * the component in the other locale. The
25584 	 * default value for the missing component is determined using the likely locales
25585 	 * data. (See getLikelyLocale()) For example, "en-US" and "en-Latn-US" receive
25586 	 * a high script score because the default script for "en" is "Latn".
25587 	 * </ul>
25588 	 *
25589 	 * The intention of this method is that it can be used to determine
25590 	 * compatibility of locales. For example, when a user signs up for an
25591 	 * account on a web site, the locales that the web site supports and
25592 	 * the locale of the user's browser may differ, and the site needs to
25593 	 * pick the best locale to show the user. Let's say the
25594 	 * web site supports a selection of European languages such as "it-IT",
25595 	 * "fr-FR", "de-DE", and "en-GB". The user's
25596 	 * browser may be set to "it-CH". The web site code can then match "it-CH"
25597 	 * against each of the supported locales to find the one with the
25598 	 * highest score. In
25599 	 * this case, the best match would be "it-IT" because it shares a
25600 	 * language and script in common with "it-CH" and differs only in the region
25601 	 * component. It is not a 100% match, but it is pretty good. The web site
25602 	 * may decide if the match scores all fall
25603 	 * below a chosen threshold (perhaps 50%?), it should show the user the
25604 	 * default language "en-GB", because that is probably a better choice
25605 	 * than any other supported locale.<p>
25606 	 *
25607 	 * @param {Locale} locale the other locale to match against the current one
25608 	 * @return {number} an integer from 0 to 100 that indicates the degree to
25609 	 * which these locales match each other
25610 	 */
25611 	match: function(locale) {
25612 		var other = new Locale(locale);
25613 		var scores = [0, 0, 0, 0];
25614 		var thisfull, otherfull, i;
25615 		
25616 		if (this.locale.language === other.language) {
25617 			scores[0] = 100;
25618 		} else {
25619 			if (!this.locale.language || !other.language) {
25620 				// check for default language
25621 				thisfull = this.getLikelyLocale();
25622 				otherfull = new Locale(this.info.likelyLocales[other.getSpec()] || other.getSpec());
25623 				if (thisfull.language === otherfull.language) {
25624 					scores[0] = 100;
25625 				}
25626 			} else {
25627 				// check for macro languages
25628 				var mlthis = this.info.macroLanguagesReverse[this.locale.language] || this.locale.language;
25629 				var mlother = this.info.macroLanguagesReverse[other.language] || other.language;
25630 				if (mlthis === mlother) {
25631 					scores[0] = 90;
25632 				} else {
25633 					// check for mutual intelligibility
25634 					var pair = this.locale.language + "-" + other.language;
25635 					scores[0] = this.info.mutualIntelligibility[pair] || 0;
25636 				}
25637 			}
25638 		}
25639 
25640 		if (this.locale.script === other.script) {
25641 			scores[1] = 100;
25642 		} else {
25643 			if (!this.locale.script || !other.script) {
25644 				// check for default script
25645 				thisfull = this.locale.script ? this.locale : new Locale(this.info.likelyLocales[this.locale.language]);
25646 				otherfull = other.script ? other : new Locale(this.info.likelyLocales[other.language]);
25647 				if (thisfull.script === otherfull.script) {
25648 					scores[1] = 100;
25649 				}
25650 			}
25651 		}
25652 
25653 		if (this.locale.region === other.region) {
25654 			scores[2] = 100;
25655 		} else {
25656 			if (!this.locale.region || !other.region) {
25657 				// check for default region
25658 				thisfull = this.getLikelyLocale();
25659 				otherfull = new Locale(this.info.likelyLocales[other.getSpec()] || other.getSpec());
25660 				if (thisfull.region === otherfull.region) {
25661 					scores[2] = 100;
25662 				}
25663 			} else {
25664 				// check for containment
25665 				var containers = this.info.territoryContainmentReverse[this.locale.region] || [];
25666 				// end at 1 because 0 is "001" which is "the whole world" -- which is not useful
25667 				for (i = containers.length-1; i > 0; i--) {
25668 					var container = this.info.territoryContainment[containers[i]];
25669 					if (container && container.indexOf(other.region) > -1) {
25670 						// same area only accounts for 20% of the region score
25671 						scores[2] = ((i+1) * 100 / containers.length) * 0.2;
25672 						break;
25673 					}
25674 				}
25675 			}
25676 		}
25677 
25678 		if (this.locale.variant === other.variant) {
25679 			scores[3] = 100;
25680 		}
25681 
25682 		var total = 0;
25683 
25684 		for (i = 0; i < 4; i++) {
25685 			total += scores[i] * componentWeights[i];
25686 		}
25687 
25688 		return Math.round(total);
25689 	},
25690 
25691     /**
25692      * Return the macrolanguage associated with this locale. If the
25693      * locale's language is not part of a macro-language, then the
25694      * locale's language is returned as-is.
25695      *
25696      * @returns {string} the ISO code for the macrolanguage associated
25697      * with this locale, or language of the locale
25698      */
25699     getMacroLanguage: function() {
25700         return this.info.macroLanguagesReverse[this.locale.language] || this.locale.language;
25701     },
25702 
25703     /**
25704      * @private
25705      * Return the containment array for the given region code.
25706      */
25707     _getRegionContainment: function(region) {
25708         return this.info.territoryContainmentReverse[region] || []
25709     },
25710 
25711     /**
25712      * Return the list of regions that this locale is contained within. Regions are
25713      * nested, so locales can be in multiple regions. (eg. US is in Northern North
25714      * America, North America, the Americas, the World.) Most regions are specified
25715      * using UN.49 region numbers, though some, like "EU", are letters. If the
25716      * locale is underspecified, this method will use the most likely locale method
25717      * to get the region first. For example, the locale "ja" (Japanese) is most
25718      * likely "ja-JP" (Japanese for Japan), and the region containment info for Japan
25719      * is returned.
25720      *
25721      * @returns {Array.<string>} an array of region specifiers that this locale is within
25722      */
25723     getRegionContainment: function() {
25724         var region = this.locale.region || this.getLikelyLocale().region;
25725         return this._getRegionContainment(region);
25726     },
25727 
25728     /**
25729      * Find the smallest region that contains both the current locale and the other locale.
25730      * If the current or other locales are underspecified, this method will use the most
25731      * likely locale method
25732      * to get their regions first. For example, the locale "ja" (Japanese) is most
25733      * likely "ja-JP" (Japanese for Japan), and the region containment info for Japan
25734      * is checked against the other locale's region containment info.
25735      *
25736      * @param {string|Locale} otherLocale a locale specifier or a Locale instance to
25737      * compare against
25738      * @returns {string} the region specifier of the smallest region containing both the
25739      * current locale and other locale
25740      */
25741     smallestCommonRegion: function(otherLocale) {
25742         if (typeof(otherLocale) === "undefined") return "001";
25743 
25744         var thisRegion = this.locale.region || this.getLikelyLocale().region;
25745         var otherLoc = typeof(otherLocale) === "string" ? new Locale(otherLocale) : otherLocale;
25746         var otherRegion = this._getLikelyLocale(otherLoc).region;
25747 
25748         var thisRegions = this._getRegionContainment(thisRegion);
25749         var otherRegions = this._getRegionContainment(otherRegion);
25750 
25751         // region containment arrays are arranged from largest to smallest, so start
25752         // at the end of the array
25753         for (var i = thisRegions.length-1; i > 0; i--) {
25754             if (otherRegions.indexOf(thisRegions[i]) > -1) {
25755                 return thisRegions[i];
25756             }
25757         }
25758 
25759         // this default should never be reached because the world should be common to all regions
25760         return "001";
25761     }
25762 };
25763 
25764 
25765 
25766 /*< CaseMapper.js */
25767 /*
25768  * caseMapper.js - define upper- and lower-case mapper
25769  * 
25770  * Copyright © 2014-2015, JEDLSoft
25771  *
25772  * Licensed under the Apache License, Version 2.0 (the "License");
25773  * you may not use this file except in compliance with the License.
25774  * You may obtain a copy of the License at
25775  *
25776  *     http://www.apache.org/licenses/LICENSE-2.0
25777  *
25778  * Unless required by applicable law or agreed to in writing, software
25779  * distributed under the License is distributed on an "AS IS" BASIS,
25780  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25781  *
25782  * See the License for the specific language governing permissions and
25783  * limitations under the License.
25784  */
25785 
25786 // !depends Locale.js IString.js
25787 
25788 
25789 
25790 /**
25791  * @class
25792  * Create a new string mapper instance that maps strings to upper or
25793  * lower case. This mapping will work for any string as characters 
25794  * that have no case will be returned unchanged.<p>
25795  * 
25796  * The options may contain any of the following properties:
25797  * 
25798  * <ul>
25799  * <li><i>locale</i> - locale to use when loading the mapper. Some maps are 
25800  * locale-dependent, and this locale selects the right one. Default if this is
25801  * not specified is the current locale.
25802  * 
25803  * <li><i>direction</i> - "toupper" for upper-casing, or "tolower" for lower-casing.
25804  * Default if not specified is "toupper".
25805  * </ul>
25806  * 
25807  * 
25808  * @constructor
25809  * @param {Object=} options options to initialize this mapper 
25810  */
25811 var CaseMapper = function (options) {
25812 	this.up = true;
25813 	this.locale = new Locale();
25814 	
25815 	if (options) {
25816 		if (typeof(options.locale) !== 'undefined') {
25817 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
25818 		}
25819 		
25820 		this.up = (!options.direction || options.direction === "toupper");
25821 	}
25822 
25823 	this.mapData = this.up ? {
25824 		"ß": "SS",		// German
25825 		'ΐ': 'Ι',		// Greek
25826 		'ά': 'Α',
25827 		'έ': 'Ε',
25828 		'ή': 'Η',
25829 		'ί': 'Ι',
25830 		'ΰ': 'Υ',
25831 		'ϊ': 'Ι',
25832 		'ϋ': 'Υ',
25833 		'ό': 'Ο',
25834 		'ύ': 'Υ',
25835 		'ώ': 'Ω',
25836 		'Ӏ': 'Ӏ',		// Russian and slavic languages
25837 		'ӏ': 'Ӏ'
25838 	} : {
25839 		'Ӏ': 'Ӏ'		// Russian and slavic languages
25840 	};
25841 
25842 	switch (this.locale.getLanguage()) {
25843 		case "az":
25844 		case "tr":
25845 		case "crh":
25846 		case "kk":
25847 		case "krc":
25848 		case "tt":
25849 			var lower = "iı";
25850 			var upper = "İI";
25851 			this._setUpMap(lower, upper);
25852 			break;
25853 	}
25854 	
25855 	if (ilib._getBrowser() === "ie" || ilib._getBrowser() === "Edge") {
25856 		// IE is missing these mappings for some reason
25857 		if (this.up) {
25858 			this.mapData['ς'] = 'Σ';
25859 		}
25860 		this._setUpMap("ⲁⲃⲅⲇⲉⲋⲍⲏⲑⲓⲕⲗⲙⲛⲝⲟⲡⲣⲥⲧⲩⲫⲭⲯⲱⳁⳉⳋ", "ⲀⲂⲄⲆⲈⲊⲌⲎⲐⲒⲔⲖⲘⲚⲜⲞⲠⲢⲤⲦⲨⲪⲬⲮⲰⳀⳈⳊ"); // Coptic
25861 		// Georgian Nuskhuri <-> Asomtavruli
25862 		this._setUpMap("ⴀⴁⴂⴃⴄⴅⴆⴇⴈⴉⴊⴋⴌⴍⴎⴏⴐⴑⴒⴓⴔⴕⴖⴗⴘⴙⴚⴛⴜⴝⴞⴟⴠⴡⴢⴣⴤⴥ", "ႠႡႢႣႤႥႦႧႨႩႪႫႬႭႮႯႰႱႲႳႴႵႶႷႸႹႺႻႼႽႾႿჀჁჂჃჄჅ");	
25863 	}
25864 };
25865 
25866 CaseMapper.prototype = {
25867 	/** 
25868 	 * @private 
25869 	 */
25870 	_charMapper: function(string) {
25871 		if (!string) {
25872 			return string;
25873 		}
25874 		var input = (typeof(string) === 'string') ? new IString(string) : string.toString();
25875 		var ret = "";
25876 		var it = input.charIterator();
25877 		var c;
25878 		
25879 		while (it.hasNext()) {
25880 			c = it.next();
25881 			if (!this.up && c === 'Σ') {
25882 				if (it.hasNext()) {
25883 					c = it.next();
25884 					var code = c.charCodeAt(0);
25885 					// if the next char is not a greek letter, this is the end of the word so use the
25886 					// final form of sigma. Otherwise, use the mid-word form.
25887 					ret += ((code < 0x0388 && code !== 0x0386) || code > 0x03CE) ? 'ς' : 'σ';
25888 					ret += c.toLowerCase();
25889 				} else {
25890 					// no next char means this is the end of the word, so use the final form of sigma
25891 					ret += 'ς';
25892 				}
25893 			} else {
25894 				if (this.mapData[c]) {
25895 					ret += this.mapData[c];
25896 				} else {
25897 					ret += this.up ? c.toUpperCase() : c.toLowerCase();
25898 				}
25899 			}
25900 		}
25901 		
25902 		return ret;
25903 	},
25904 
25905 	/** @private */
25906 	_setUpMap: function(lower, upper) {
25907 		var from, to;
25908 		if (this.up) {
25909 			from = lower;
25910 			to = upper;
25911 		} else {
25912 			from = upper;
25913 			to = lower;
25914 		}
25915 		for (var i = 0; i < upper.length; i++) {
25916 			this.mapData[from[i]] = to[i];
25917 		}
25918 	},
25919 
25920 	/**
25921 	 * Return the locale that this mapper was constructed with. 
25922 	 * @returns {Locale} the locale that this mapper was constructed with
25923 	 */
25924 	getLocale: function () {
25925 		return this.locale;
25926 	},
25927 		
25928 	/**
25929 	 * Map a string to lower case in a locale-sensitive manner.
25930 	 * 
25931 	 * @param {string|undefined} string
25932 	 * @return {string|undefined}
25933 	 */
25934 	map: function (string) {
25935 		return this._charMapper(string);
25936 	}
25937 };
25938 
25939 
25940 
25941 /*< NumberingPlan.js */
25942 /*
25943  * NumPlan.js - Represent a phone numbering plan.
25944  * 
25945  * Copyright © 2014-2015, JEDLSoft
25946  *
25947  * Licensed under the Apache License, Version 2.0 (the "License");
25948  * you may not use this file except in compliance with the License.
25949  * You may obtain a copy of the License at
25950  *
25951  *     http://www.apache.org/licenses/LICENSE-2.0
25952  *
25953  * Unless required by applicable law or agreed to in writing, software
25954  * distributed under the License is distributed on an "AS IS" BASIS,
25955  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25956  *
25957  * See the License for the specific language governing permissions and
25958  * limitations under the License.
25959  */
25960 
25961 /*
25962 !depends 
25963 ilib.js 
25964 Locale.js 
25965 Utils.js
25966 */
25967 
25968 // !data numplan
25969 
25970 
25971 /**
25972  * @class
25973  * Create a numbering plan information instance for a particular country's plan.<p>
25974  * 
25975  * The options may contain any of the following properties:
25976  * 
25977  * <ul>
25978  * <li><i>locale</i> - locale for which the numbering plan is sought. This locale
25979  * will be mapped to the actual numbering plan, which may be shared amongst a
25980  * number of countries.
25981  *
25982  * <li>onLoad - a callback function to call when the date format object is fully 
25983  * loaded. When the onLoad option is given, the DateFmt object will attempt to
25984  * load any missing locale data using the ilib loader callback.
25985  * When the constructor is done (even if the data is already preassembled), the 
25986  * onLoad function is called with the current instance as a parameter, so this
25987  * callback can be used with preassembled or dynamic loading or a mix of the two.
25988  * 
25989  * <li>sync - tell whether to load any missing locale data synchronously or 
25990  * asynchronously. If this option is given as "false", then the "onLoad"
25991  * callback must be given, as the instance returned from this constructor will
25992  * not be usable for a while.
25993  *  
25994  * <li><i>loadParams</i> - an object containing parameters to pass to the 
25995  * loader callback function when locale data is missing. The parameters are not
25996  * interpretted or modified in any way. They are simply passed along. The object 
25997  * may contain any property/value pairs as long as the calling code is in
25998  * agreement with the loader callback function as to what those parameters mean.
25999  * </ul>
26000  * 
26001  * @private
26002  * @constructor
26003  * @param {Object} options options governing the way this plan is loaded
26004  */
26005 var NumberingPlan = function (options) {
26006 	var sync = true,
26007 	    loadParams = {};
26008 	
26009 	this.locale = new Locale();
26010 
26011 	if (options) {
26012 		if (options.locale) {
26013 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
26014 		}
26015 		
26016 		if (typeof(options.sync) !== 'undefined') {
26017 			sync = !!options.sync;
26018 		}
26019 		
26020 		if (options.loadParams) {
26021 			loadParams = options.loadParams;
26022 		}
26023 	}	
26024 
26025 	Utils.loadData({
26026 		name: "numplan.json",
26027 		object: "NumberingPlan",
26028 		locale: this.locale,
26029 		sync: sync, 
26030 		loadParams: loadParams, 
26031 		callback: ilib.bind(this, function (npdata) {
26032 			if (!npdata) {
26033 				npdata = {
26034 					"region": "XX",
26035 					"skipTrunk": false,
26036 					"trunkCode": "0",
26037 					"iddCode": "00",
26038 					"dialingPlan": "closed",
26039 					"commonFormatChars": " ()-./",
26040 					"fieldLengths": {
26041 						"areaCode": 0,
26042 						"cic": 0,
26043 						"mobilePrefix": 0,
26044 						"serviceCode": 0
26045 					}
26046 				};
26047 			}
26048 
26049 			/** 
26050 			 * @type {{
26051 			 *   region:string,
26052 			 *   skipTrunk:boolean,
26053 			 *   trunkCode:string,
26054 			 *   iddCode:string,
26055 			 *   dialingPlan:string,
26056 			 *   commonFormatChars:string,
26057 			 *   fieldLengths:Object.<string,number>,
26058 			 *   contextFree:boolean,
26059 			 *   findExtensions:boolean,
26060 			 *   trunkRequired:boolean,
26061 			 *   extendedAreaCodes:boolean
26062 			 * }}
26063 			 */
26064 			this.npdata = npdata;
26065 			if (options && typeof(options.onLoad) === 'function') {
26066 				options.onLoad(this);
26067 			}
26068 		})
26069 	});
26070 };
26071 
26072 NumberingPlan.prototype = {
26073 	/**
26074 	 * Return the name of this plan. This may be different than the 
26075 	 * name of the region because sometimes multiple countries share 
26076 	 * the same plan.
26077 	 * @return {string} the name of the plan
26078 	 */
26079 	getName: function() {
26080 		return this.npdata.region;
26081 	},
26082 
26083 	/**
26084 	 * Return the trunk code of the current plan as a string.
26085 	 * @return {string|undefined} the trunk code of the plan or
26086 	 * undefined if there is no trunk code in this plan
26087 	 */
26088 	getTrunkCode: function() {
26089 		return this.npdata.trunkCode;
26090 	},
26091 	
26092 	/**
26093 	 * Return the international direct dialing code of this plan.
26094 	 * @return {string} the IDD code of this plan
26095 	 */
26096 	getIDDCode: function() {
26097 		return this.npdata.iddCode;	
26098 	},
26099 	
26100 	/**
26101 	 * Return the plan style for this plan. The plan style may be
26102 	 * one of:
26103 	 * 
26104 	 * <ul>
26105 	 * <li>"open" - area codes may be left off if the caller is 
26106 	 * dialing to another number within the same area code
26107 	 * <li>"closed" - the area code must always be specified, even
26108 	 * if calling another number within the same area code
26109 	 * </ul>
26110 	 * 
26111 	 * @return {string} the plan style, "open" or "closed"
26112 	 */
26113 	getPlanStyle: function() {	
26114 		return this.npdata.dialingPlan;
26115 	},
26116 	/** [Need Comment]
26117 	 * Return a contextFree
26118 	 *
26119 	 * @return {boolean}
26120 	 */
26121 	getContextFree: function() {
26122 		return this.npdata.contextFree;
26123 	},
26124 	/** [Need Comment]
26125 	 * Return a findExtensions
26126 	 * 
26127 	 * @return {boolean}
26128 	 */
26129 	getFindExtensions: function() {
26130 		return this.npdata.findExtensions;
26131 	},
26132 	/** [Need Comment]
26133 	 * Return a skipTrunk
26134 	 * 
26135 	 * @return {boolean}
26136 	 */
26137 	getSkipTrunk: function() {
26138 		return this.npdata.skipTrunk;
26139 	},
26140 	/** [Need Comment]
26141 	 * Return a skipTrunk
26142 	 * 
26143 	 * @return {boolean}
26144 	 */
26145 	getTrunkRequired: function() {
26146 		return this.npdata.trunkRequired;
26147 	},
26148 	/**
26149 	 * Return true if this plan uses extended area codes.
26150 	 * @return {boolean} true if the plan uses extended area codes
26151 	 */
26152 	getExtendedAreaCode: function() {
26153 		return this.npdata.extendedAreaCodes;
26154 	},
26155 	/**
26156 	 * Return a string containing all of the common format characters
26157 	 * used to format numbers.
26158 	 * @return {string} the common format characters fused in this locale
26159 	 */
26160 	getCommonFormatChars: function() {
26161 		return this.npdata.commonFormatChars;
26162 	},
26163 	
26164 	/**
26165 	 * Return the length of the field with the given name. If the length
26166 	 * is returned as 0, this means it is variable length.
26167 	 * 
26168 	 * @param {string} field name of the field for which the length is 
26169 	 * being sought
26170 	 * @return {number} if positive, this gives the length of the given 
26171 	 * field. If zero, the field is variable length. If negative, the
26172 	 * field is not known.
26173 	 */
26174 	getFieldLength: function (field) {
26175 		var dataField = this.npdata.fieldLengths;
26176 		
26177 		return dataField[field];
26178 	}
26179 };
26180 
26181 
26182 /*< PhoneLocale.js */
26183 /*
26184  * phoneloc.js - Represent a phone locale object.
26185  * 
26186  * Copyright © 2014-2015, JEDLSoft
26187  *
26188  * Licensed under the Apache License, Version 2.0 (the "License");
26189  * you may not use this file except in compliance with the License.
26190  * You may obtain a copy of the License at
26191  *
26192  *     http://www.apache.org/licenses/LICENSE-2.0
26193  *
26194  * Unless required by applicable law or agreed to in writing, software
26195  * distributed under the License is distributed on an "AS IS" BASIS,
26196  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26197  *
26198  * See the License for the specific language governing permissions and
26199  * limitations under the License.
26200  */
26201 
26202 /*
26203 !depends 
26204 ilib.js 
26205 Locale.js
26206 Utils.js
26207 */
26208 
26209 // !data phoneloc
26210 
26211 
26212 /**
26213  * @class
26214  * Extension of the locale class that has extra methods to map various numbers
26215  * related to phone number parsing.
26216  *
26217  * @param {Object} options Options that govern how this phone locale works
26218  * 
26219  * @private
26220  * @constructor
26221  * @extends Locale
26222  */
26223 var PhoneLocale = function(options) {
26224 	var region,
26225 		mcc,
26226 		cc,
26227 		sync = true,
26228 		loadParams = {},
26229 		locale;
26230 	
26231 	locale = (options && options.locale) || ilib.getLocale();
26232 
26233 	this.parent.call(this, locale);
26234 	
26235 	region = this.region;
26236 	
26237 	if (options) {
26238 		if (typeof(options.mcc) !== 'undefined') {
26239 			mcc = options.mcc;
26240 		}
26241 		
26242 		if (typeof(options.countryCode) !== 'undefined') {
26243 			cc = options.countryCode;
26244 		}
26245 
26246 		if (typeof(options.sync) !== 'undefined') {
26247 			sync = !!options.sync;
26248 		}
26249 		
26250 		if (options.loadParams) {
26251 			loadParams = options.loadParams;
26252 		}
26253 	}
26254 
26255 	Utils.loadData({
26256 		name: "phoneloc.json",
26257 		object: "PhoneLocale",
26258 		nonlocale: true,
26259 		sync: sync, 
26260 		loadParams: loadParams, 
26261 		callback: ilib.bind(this, function (data) {
26262 			/** @type {{mcc2reg:Object.<string,string>,cc2reg:Object.<string,string>,reg2cc:Object.<string,string>,area2reg:Object.<string,string>}} */
26263 			this.mappings = data;
26264 			
26265 			if (typeof(mcc) !== 'undefined') {
26266 				region = this.mappings.mcc2reg[mcc];	
26267 			}
26268 
26269 			if (typeof(cc) !== 'undefined') {
26270 				region = this.mappings.cc2reg[cc];
26271 			}
26272 
26273 			if (!region) {
26274 				region = "XX";
26275 			}
26276 
26277 			this.region = this._normPhoneReg(region);
26278 			this._genSpec();
26279 
26280 			if (options && typeof(options.onLoad) === 'function') {
26281 				options.onLoad(this);
26282 			}									
26283 		})
26284 	});
26285 };
26286 
26287 PhoneLocale.prototype = new Locale();
26288 PhoneLocale.prototype.parent = Locale;
26289 PhoneLocale.prototype.constructor = PhoneLocale;
26290 
26291 /**
26292  * Map a mobile carrier code to a region code.
26293  *
26294  * @static
26295  * @package
26296  * @param {string|undefined} mcc the MCC to map
26297  * @return {string|undefined} the region code
26298  */
26299 
26300 PhoneLocale.prototype._mapMCCtoRegion = function(mcc) {
26301 	if (!mcc) {
26302 		return undefined;
26303 	}
26304 	return this.mappings.mcc2reg && this.mappings.mcc2reg[mcc] || "XX";
26305 };
26306 
26307 /**
26308  * Map a country code to a region code.
26309  *
26310  * @static
26311  * @package
26312  * @param {string|undefined} cc the country code to map
26313  * @return {string|undefined} the region code
26314  */
26315 PhoneLocale.prototype._mapCCtoRegion = function(cc) {
26316 	if (!cc) {
26317 		return undefined;
26318 	}
26319 	return this.mappings.cc2reg && this.mappings.cc2reg[cc] || "XX";
26320 };
26321 
26322 /**
26323  * Map a region code to a country code.
26324  *
26325  * @static
26326  * @package
26327  * @param {string|undefined} region the region code to map
26328  * @return {string|undefined} the country code
26329  */
26330 PhoneLocale.prototype._mapRegiontoCC = function(region) {
26331 	if (!region) {
26332 		return undefined;
26333 	}
26334 	return this.mappings.reg2cc && this.mappings.reg2cc[region] || "0";
26335 };
26336 
26337 /**
26338  * Map a country code to a region code.
26339  *
26340  * @static
26341  * @package
26342  * @param {string|undefined} cc the country code to map
26343  * @param {string|undefined} area the area code within the country code's numbering plan
26344  * @return {string|undefined} the region code
26345  */
26346 PhoneLocale.prototype._mapAreatoRegion = function(cc, area) {
26347 	if (!cc) {
26348 		return undefined;
26349 	}
26350 	if (cc in this.mappings.area2reg) {
26351 		return this.mappings.area2reg[cc][area] || this.mappings.area2reg[cc]["default"];
26352 	} else {
26353 		return this.mappings.cc2reg[cc];
26354 	}
26355 };
26356 
26357 /**
26358  * Return the region that controls the dialing plan in the given
26359  * region. (ie. the "normalized phone region".)
26360  * 
26361  * @static
26362  * @package
26363  * @param {string} region the region code to normalize
26364  * @return {string} the normalized region code
26365  */
26366 PhoneLocale.prototype._normPhoneReg = function(region) {
26367 	var norm;
26368 	
26369 	// Map all NANP regions to the right region, so that they get parsed using the 
26370 	// correct state table
26371 	switch (region) {
26372 		case "US": // usa
26373 		case "CA": // canada
26374 		case "AG": // antigua and barbuda
26375 		case "BS": // bahamas
26376 		case "BB": // barbados
26377 		case "DM": // dominica
26378 		case "DO": // dominican republic
26379 		case "GD": // grenada
26380 		case "JM": // jamaica
26381 		case "KN": // st. kitts and nevis
26382 		case "LC": // st. lucia
26383 		case "VC": // st. vincent and the grenadines
26384 		case "TT": // trinidad and tobago
26385 		case "AI": // anguilla
26386 		case "BM": // bermuda
26387 		case "VG": // british virgin islands
26388 		case "KY": // cayman islands
26389 		case "MS": // montserrat
26390 		case "TC": // turks and caicos
26391 		case "AS": // American Samoa 
26392 		case "VI": // Virgin Islands, U.S.
26393 		case "PR": // Puerto Rico
26394 		case "MP": // Northern Mariana Islands
26395 		case "T:": // East Timor
26396 		case "GU": // Guam
26397 			norm = "US";
26398 			break;
26399 		
26400 		// these all use the Italian dialling plan
26401 		case "IT": // italy
26402 		case "SM": // san marino
26403 		case "VA": // vatican city
26404 			norm = "IT";
26405 			break;
26406 		
26407 		// all the French dependencies are on the French dialling plan
26408 		case "FR": // france
26409 		case "GF": // french guiana
26410 		case "MQ": // martinique
26411 		case "GP": // guadeloupe, 
26412 		case "BL": // saint barthélemy
26413 		case "MF": // saint martin
26414 		case "RE": // réunion, mayotte
26415 			norm = "FR";
26416 			break;
26417 		default:
26418 			norm = region;
26419 			break;
26420 	}	
26421 	return norm;
26422 };
26423 
26424 
26425 /*< PhoneHandlerFactory.js */
26426 /*
26427  * handler.js - Handle phone number parse states
26428  * 
26429  * Copyright © 2014-2015, JEDLSoft
26430  *
26431  * Licensed under the Apache License, Version 2.0 (the "License");
26432  * you may not use this file except in compliance with the License.
26433  * You may obtain a copy of the License at
26434  *
26435  *     http://www.apache.org/licenses/LICENSE-2.0
26436  *
26437  * Unless required by applicable law or agreed to in writing, software
26438  * distributed under the License is distributed on an "AS IS" BASIS,
26439  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26440  *
26441  * See the License for the specific language governing permissions and
26442  * limitations under the License.
26443  */
26444 
26445 
26446 /**
26447  * @class
26448  * @private
26449  * @constructor
26450  */
26451 var PhoneHandler = function () {
26452 	return this;
26453 };
26454 
26455 PhoneHandler.prototype = {
26456 	/**
26457 	 * @private
26458 	 * @param {string} number phone number
26459 	 * @param {Object} fields the fields that have been extracted so far
26460 	 * @param {Object} regionSettings settings used to parse the rest of the number
26461 	 */
26462 	processSubscriberNumber: function(number, fields, regionSettings) {
26463 		var last;
26464 		
26465 		last = number.search(/[xwtp,;]/i);	// last digit of the local number
26466 
26467 		if (last > -1) {
26468 			if (last > 0) {
26469 				fields.subscriberNumber = number.substring(0, last);
26470 			}
26471 			// strip x's which are there to indicate a break between the local subscriber number and the extension, but
26472 			// are not themselves a dialable character
26473 			fields.extension = number.substring(last).replace('x', '');
26474 		} else {
26475 			if (number.length) {
26476 				fields.subscriberNumber = number;
26477 			}
26478 		}
26479 		
26480 		if (regionSettings.plan.getFieldLength('maxLocalLength') &&
26481 				fields.subscriberNumber &&
26482 				fields.subscriberNumber.length > regionSettings.plan.getFieldLength('maxLocalLength')) {
26483 			fields.invalid = true;
26484 		}
26485 	},
26486 	/**
26487 	 * @private
26488 	 * @param {string} fieldName 
26489 	 * @param {number} length length of phone number
26490 	 * @param {string} number phone number
26491 	 * @param {number} currentChar currentChar to be parsed
26492 	 * @param {Object} fields the fields that have been extracted so far
26493 	 * @param {Object} regionSettings settings used to parse the rest of the number
26494 	 * @param {boolean} noExtractTrunk 
26495 	 */
26496 	processFieldWithSubscriberNumber: function(fieldName, length, number, currentChar, fields, regionSettings, noExtractTrunk) {
26497 		var ret, end;
26498 		
26499 		if (length !== undefined && length > 0) {
26500 			// fixed length
26501 			end = length;
26502 			if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") {
26503 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
26504 			}
26505 		} else {
26506 			// variable length
26507 			// the setting is the negative of the length to add, so subtract to make it positive
26508 			end = currentChar + 1 - length;
26509 		}
26510 		
26511 		if (fields[fieldName] !== undefined) {
26512 			// we have a spurious recognition, because this number already contains that field! So, just put
26513 			// everything into the subscriberNumber as the default
26514 			this.processSubscriberNumber(number, fields, regionSettings);
26515 		} else {
26516 			fields[fieldName] = number.substring(0, end);
26517 			if (number.length > end) {
26518 				this.processSubscriberNumber(number.substring(end), fields, regionSettings);
26519 			}
26520 		}
26521 		
26522 		ret = {
26523 			number: ""
26524 		};
26525 
26526 		return ret;
26527 	},
26528 	/**
26529 	 * @private
26530 	 * @param {string} fieldName 
26531 	 * @param {number} length length of phone number
26532 	 * @param {string} number phone number
26533 	 * @param {number} currentChar currentChar to be parsed
26534 	 * @param {Object} fields the fields that have been extracted so far
26535 	 * @param {Object} regionSettings settings used to parse the rest of the number
26536 	 */
26537 	processField: function(fieldName, length, number, currentChar, fields, regionSettings) {
26538 		var ret = {}, end;
26539 		
26540 		if (length !== undefined && length > 0) {
26541 			// fixed length
26542 			end = length;
26543 			if (regionSettings.plan.getTrunkCode() === "0" && number.charAt(0) === "0") {
26544 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
26545 			}
26546 		} else {
26547 			// variable length
26548 			// the setting is the negative of the length to add, so subtract to make it positive
26549 			end = currentChar + 1 - length;
26550 		}
26551 		
26552 		if (fields[fieldName] !== undefined) {
26553 			// we have a spurious recognition, because this number already contains that field! So, just put
26554 			// everything into the subscriberNumber as the default
26555 			this.processSubscriberNumber(number, fields, regionSettings);
26556 			ret.number = "";
26557 		} else {
26558 			fields[fieldName] = number.substring(0, end);			
26559 			ret.number = (number.length > end) ? number.substring(end) : "";
26560 		}
26561 		
26562 		return ret;
26563 	},
26564 	/**
26565 	 * @private
26566 	 * @param {string} number phone number
26567 	 * @param {number} currentChar currentChar to be parsed
26568 	 * @param {Object} fields the fields that have been extracted so far
26569 	 * @param {Object} regionSettings settings used to parse the rest of the number
26570 	 */
26571 	trunk: function(number, currentChar, fields, regionSettings) {
26572 		var ret, trunkLength;
26573 		
26574 		if (fields.trunkAccess !== undefined) {
26575 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
26576 			this.processSubscriberNumber(number, fields, regionSettings);
26577 			number = "";
26578 		} else {
26579 			trunkLength = regionSettings.plan.getTrunkCode().length;
26580 			fields.trunkAccess = number.substring(0, trunkLength);
26581 			number = (number.length > trunkLength) ? number.substring(trunkLength) : "";
26582 		}
26583 		
26584 		ret = {
26585 			number: number
26586 		};
26587 		
26588 		return ret;
26589 	},
26590 	/**
26591 	 * @private
26592 	 * @param {string} number phone number
26593 	 * @param {number} currentChar currentChar to be parsed
26594 	 * @param {Object} fields the fields that have been extracted so far
26595 	 * @param {Object} regionSettings settings used to parse the rest of the number
26596 	 */
26597 	plus: function(number, currentChar, fields, regionSettings) {
26598 		var ret = {};
26599 		
26600 		if (fields.iddPrefix !== undefined) {
26601 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
26602 			this.processSubscriberNumber(number, fields, regionSettings);
26603 			ret.number = "";
26604 		} else {
26605 			// found the idd prefix, so save it and cause the function to parse the next part
26606 			// of the number with the idd table
26607 			fields.iddPrefix = number.substring(0, 1);
26608 	
26609 			ret = {
26610 				number: number.substring(1),
26611 				table: 'idd'    // shared subtable that parses the country code
26612 			};
26613 		}		
26614 		return ret;
26615 	},
26616 	/**
26617 	 * @private
26618 	 * @param {string} number phone number
26619 	 * @param {number} currentChar currentChar to be parsed
26620 	 * @param {Object} fields the fields that have been extracted so far
26621 	 * @param {Object} regionSettings settings used to parse the rest of the number
26622 	 */
26623 	idd: function(number, currentChar, fields, regionSettings) {
26624 		var ret = {};
26625 		
26626 		if (fields.iddPrefix !== undefined) {
26627 			// What? We already have one? Okay, put the rest of this in the subscriber number as the default behaviour then.
26628 			this.processSubscriberNumber(number, fields, regionSettings);
26629 			ret.number = "";
26630 		} else {
26631 			// found the idd prefix, so save it and cause the function to parse the next part
26632 			// of the number with the idd table
26633 			fields.iddPrefix = number.substring(0, currentChar+1);
26634 	
26635 			ret = {
26636 				number: number.substring(currentChar+1),
26637 				table: 'idd'    // shared subtable that parses the country code
26638 			};
26639 		}
26640 		
26641 		return ret;
26642 	},
26643 	/**
26644 	 * @private
26645 	 * @param {string} number phone number
26646 	 * @param {number} currentChar currentChar to be parsed
26647 	 * @param {Object} fields the fields that have been extracted so far
26648 	 * @param {Object} regionSettings settings used to parse the rest of the number
26649 	 */	
26650 	country: function(number, currentChar, fields, regionSettings) {
26651 		var ret, cc;
26652 		
26653 		// found the country code of an IDD number, so save it and cause the function to 
26654 		// parse the rest of the number with the regular table for this locale
26655 		fields.countryCode = number.substring(0, currentChar+1);
26656 		cc = fields.countryCode.replace(/[wWpPtT\+#\*]/g, ''); // fix for NOV-108200
26657 		// console.log("Found country code " + fields.countryCode + ". Switching to country " + locale.region + " to parse the rest of the number");
26658 		
26659 		ret = {
26660 			number: number.substring(currentChar+1),
26661 			countryCode: cc
26662 		};
26663 		
26664 		return ret;
26665 	},
26666 	/**
26667 	 * @private
26668 	 * @param {string} number phone number
26669 	 * @param {number} currentChar currentChar to be parsed
26670 	 * @param {Object} fields the fields that have been extracted so far
26671 	 * @param {Object} regionSettings settings used to parse the rest of the number
26672 	 */
26673 	cic: function(number, currentChar, fields, regionSettings) {
26674 		return this.processField('cic', regionSettings.plan.getFieldLength('cic'), number, currentChar, fields, regionSettings);
26675 	},
26676 	/**
26677 	 * @private
26678 	 * @param {string} number phone number
26679 	 * @param {number} currentChar currentChar to be parsed
26680 	 * @param {Object} fields the fields that have been extracted so far
26681 	 * @param {Object} regionSettings settings used to parse the rest of the number
26682 	 */
26683 	service: function(number, currentChar, fields, regionSettings) {
26684 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('serviceCode'), number, currentChar, fields, regionSettings, false);
26685 	},
26686 	/**
26687 	 * @private
26688 	 * @param {string} number phone number
26689 	 * @param {number} currentChar currentChar to be parsed
26690 	 * @param {Object} fields the fields that have been extracted so far
26691 	 * @param {Object} regionSettings settings used to parse the rest of the number
26692 	 */
26693 	area: function(number, currentChar, fields, regionSettings) {
26694 		var ret, last, end, localLength;
26695 		
26696 		last = number.search(/[xwtp]/i);	// last digit of the local number
26697 		localLength = (last > -1) ? last : number.length;
26698 
26699 		if (regionSettings.plan.getFieldLength('areaCode') > 0) {
26700 			// fixed length
26701 			end = regionSettings.plan.getFieldLength('areaCode');
26702 			if (regionSettings.plan.getTrunkCode() === number.charAt(0)) {
26703 				end += regionSettings.plan.getTrunkCode().length;  // also extract the trunk access code
26704 				localLength -= regionSettings.plan.getTrunkCode().length;
26705 			}
26706 		} else {
26707 			// variable length
26708 			// the setting is the negative of the length to add, so subtract to make it positive
26709 			end = currentChar + 1 - regionSettings.plan.getFieldLength('areaCode');
26710 		}
26711 		
26712 		// substring() extracts the part of the string up to but not including the end character,
26713 		// so add one to compensate
26714 		if (regionSettings.plan.getFieldLength('maxLocalLength') !== undefined) {
26715 			if (fields.trunkAccess !== undefined || fields.mobilePrefix !== undefined ||
26716 					fields.countryCode !== undefined ||
26717 					localLength > regionSettings.plan.getFieldLength('maxLocalLength')) {
26718 				// too long for a local number by itself, or a different final state already parsed out the trunk
26719 				// or mobile prefix, then consider the rest of this number to be an area code + part of the subscriber number
26720 				fields.areaCode = number.substring(0, end);
26721 				if (number.length > end) {
26722 					this.processSubscriberNumber(number.substring(end), fields, regionSettings);
26723 				}
26724 			} else {
26725 				// shorter than the length needed for a local number, so just consider it a local number
26726 				this.processSubscriberNumber(number, fields, regionSettings);
26727 			}
26728 		} else {
26729 			fields.areaCode = number.substring(0, end);
26730 			if (number.length > end) {
26731 				this.processSubscriberNumber(number.substring(end), fields, regionSettings);
26732 			}
26733 		}
26734 		
26735 		// extensions are separated from the number by a dash in Germany
26736 		if (regionSettings.plan.getFindExtensions() !== undefined && fields.subscriberNumber !== undefined) {
26737 			var dash = fields.subscriberNumber.indexOf("-");
26738 			if (dash > -1) {
26739 				fields.subscriberNumber = fields.subscriberNumber.substring(0, dash);
26740 				fields.extension = fields.subscriberNumber.substring(dash+1);
26741 			}
26742 		}
26743 
26744 		ret = {
26745 			number: ""
26746 		};
26747 
26748 		return ret;
26749 	},
26750 	/**
26751 	 * @private
26752 	 * @param {string} number phone number
26753 	 * @param {number} currentChar currentChar to be parsed
26754 	 * @param {Object} fields the fields that have been extracted so far
26755 	 * @param {Object} regionSettings settings used to parse the rest of the number
26756 	 */
26757 	none: function(number, currentChar, fields, regionSettings) {
26758 		var ret;
26759 		
26760 		// this is a last resort function that is called when nothing is recognized.
26761 		// When this happens, just put the whole stripped number into the subscriber number
26762 			
26763 		if (number.length > 0) {
26764 			this.processSubscriberNumber(number, fields, regionSettings);
26765 			if (currentChar > 0 && currentChar < number.length) {
26766 				// if we were part-way through parsing, and we hit an invalid digit,
26767 				// indicate that the number could not be parsed properly
26768 				fields.invalid = true;
26769 			}
26770 		}
26771 		
26772 		ret = {
26773 			number:""
26774 		};
26775 		
26776 		return ret;
26777 	},
26778 	/**
26779 	 * @private
26780 	 * @param {string} number phone number
26781 	 * @param {number} currentChar currentChar to be parsed
26782 	 * @param {Object} fields the fields that have been extracted so far
26783 	 * @param {Object} regionSettings settings used to parse the rest of the number
26784 	 */
26785 	vsc: function(number, currentChar, fields, regionSettings) {
26786 		var ret, length, end;
26787 
26788 		if (fields.vsc === undefined) {
26789 			length = regionSettings.plan.getFieldLength('vsc') || 0;
26790 			if (length !== undefined && length > 0) {
26791 				// fixed length
26792 				end = length;
26793 			} else {
26794 				// variable length
26795 				// the setting is the negative of the length to add, so subtract to make it positive
26796 				end = currentChar + 1 - length;
26797 			}
26798 			
26799 			// found a VSC code (ie. a "star code"), so save it and cause the function to 
26800 			// parse the rest of the number with the same table for this locale
26801 			fields.vsc = number.substring(0, end);
26802 			number = (number.length > end) ? "^" + number.substring(end) : "";
26803 		} else {
26804 			// got it twice??? Okay, this is a bogus number then. Just put everything else into the subscriber number as the default
26805 			this.processSubscriberNumber(number, fields, regionSettings);
26806 			number = "";
26807 		}
26808 
26809 		// treat the rest of the number as if it were a completely new number
26810 		ret = {
26811 			number: number
26812 		};
26813 
26814 		return ret;
26815 	},
26816 	/**
26817 	 * @private
26818 	 * @param {string} number phone number
26819 	 * @param {number} currentChar currentChar to be parsed
26820 	 * @param {Object} fields the fields that have been extracted so far
26821 	 * @param {Object} regionSettings settings used to parse the rest of the number
26822 	 */
26823 	cell: function(number, currentChar, fields, regionSettings) {
26824 		return this.processFieldWithSubscriberNumber('mobilePrefix', regionSettings.plan.getFieldLength('mobilePrefix'), number, currentChar, fields, regionSettings, false);
26825 	},
26826 	/**
26827 	 * @private
26828 	 * @param {string} number phone number
26829 	 * @param {number} currentChar currentChar to be parsed
26830 	 * @param {Object} fields the fields that have been extracted so far
26831 	 * @param {Object} regionSettings settings used to parse the rest of the number
26832 	 */
26833 	personal: function(number, currentChar, fields, regionSettings) {
26834 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('personal'), number, currentChar, fields, regionSettings, false);
26835 	},
26836 	/**
26837 	 * @private
26838 	 * @param {string} number phone number
26839 	 * @param {number} currentChar currentChar to be parsed
26840 	 * @param {Object} fields the fields that have been extracted so far
26841 	 * @param {Object} regionSettings settings used to parse the rest of the number
26842 	 */
26843 	emergency: function(number, currentChar, fields, regionSettings) {
26844 		return this.processFieldWithSubscriberNumber('emergency', regionSettings.plan.getFieldLength('emergency'), number, currentChar, fields, regionSettings, true);
26845 	},
26846 	/**
26847 	 * @private
26848 	 * @param {string} number phone number
26849 	 * @param {number} currentChar currentChar to be parsed
26850 	 * @param {Object} fields the fields that have been extracted so far
26851 	 * @param {Object} regionSettings settings used to parse the rest of the number
26852 	 */
26853 	premium: function(number, currentChar, fields, regionSettings) {
26854 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('premium'), number, currentChar, fields, regionSettings, false);
26855 	},
26856 	/**
26857 	 * @private
26858 	 * @param {string} number phone number
26859 	 * @param {number} currentChar currentChar to be parsed
26860 	 * @param {Object} fields the fields that have been extracted so far
26861 	 * @param {Object} regionSettings settings used to parse the rest of the number
26862 	 */
26863 	special: function(number, currentChar, fields, regionSettings) {
26864 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('special'), number, currentChar, fields, regionSettings, false);
26865 	},
26866 	/**
26867 	 * @private
26868 	 * @param {string} number phone number
26869 	 * @param {number} currentChar currentChar to be parsed
26870 	 * @param {Object} fields the fields that have been extracted so far
26871 	 * @param {Object} regionSettings settings used to parse the rest of the number
26872 	 */
26873 	service2: function(number, currentChar, fields, regionSettings) {
26874 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service2'), number, currentChar, fields, regionSettings, false);
26875 	},
26876 	/**
26877 	 * @private
26878 	 * @param {string} number phone number
26879 	 * @param {number} currentChar currentChar to be parsed
26880 	 * @param {Object} fields the fields that have been extracted so far
26881 	 * @param {Object} regionSettings settings used to parse the rest of the number
26882 	 */
26883 	service3: function(number, currentChar, fields, regionSettings) {
26884 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service3'), number, currentChar, fields, regionSettings, false);
26885 	},
26886 	/**
26887 	 * @private
26888 	 * @param {string} number phone number
26889 	 * @param {number} currentChar currentChar to be parsed
26890 	 * @param {Object} fields the fields that have been extracted so far
26891 	 * @param {Object} regionSettings settings used to parse the rest of the number
26892 	 */
26893 	service4: function(number, currentChar, fields, regionSettings) {
26894 		return this.processFieldWithSubscriberNumber('serviceCode', regionSettings.plan.getFieldLength('service4'), number, currentChar, fields, regionSettings, false);
26895 	},
26896 	/**
26897 	 * @private
26898 	 * @param {string} number phone number
26899 	 * @param {number} currentChar currentChar to be parsed
26900 	 * @param {Object} fields the fields that have been extracted so far
26901 	 * @param {Object} regionSettings settings used to parse the rest of the number
26902 	 */
26903 	cic2: function(number, currentChar, fields, regionSettings) {
26904 		return this.processField('cic', regionSettings.plan.getFieldLength('cic2'), number, currentChar, fields, regionSettings);
26905 	},
26906 	/**
26907 	 * @private
26908 	 * @param {string} number phone number
26909 	 * @param {number} currentChar currentChar to be parsed
26910 	 * @param {Object} fields the fields that have been extracted so far
26911 	 * @param {Object} regionSettings settings used to parse the rest of the number
26912 	 */
26913 	cic3: function(number, currentChar, fields, regionSettings) {
26914 		return this.processField('cic', regionSettings.plan.getFieldLength('cic3'), number, currentChar, fields, regionSettings);
26915 	},
26916 	/**
26917 	 * @private
26918 	 * @param {string} number phone number
26919 	 * @param {number} currentChar currentChar to be parsed
26920 	 * @param {Object} fields the fields that have been extracted so far
26921 	 * @param {Object} regionSettings settings used to parse the rest of the number
26922 	 */
26923 	start: function(number, currentChar, fields, regionSettings) {
26924 		// don't do anything except transition to the next state
26925 		return {
26926 			number: number
26927 		};
26928 	},
26929 	/**
26930 	 * @private
26931 	 * @param {string} number phone number
26932 	 * @param {number} currentChar currentChar to be parsed
26933 	 * @param {Object} fields the fields that have been extracted so far
26934 	 * @param {Object} regionSettings settings used to parse the rest of the number
26935 	 */
26936 	local: function(number, currentChar, fields, regionSettings) {
26937 		// in open dialling plans, we can tell that this number is a local subscriber number because it
26938 		// starts with a digit that indicates as such
26939 		this.processSubscriberNumber(number, fields, regionSettings);
26940 		return {
26941 			number: ""
26942 		};
26943 	}
26944 };
26945 
26946 // context-sensitive handler
26947 /**
26948  * @class
26949  * @private
26950  * @extends PhoneHandler
26951  * @constructor
26952  */
26953 var CSStateHandler = function () {
26954 	return this;
26955 };
26956 
26957 CSStateHandler.prototype = new PhoneHandler();
26958 CSStateHandler.prototype.special = function (number, currentChar, fields, regionSettings) {
26959 	var ret;
26960 	
26961 	// found a special area code that is both a node and a leaf. In
26962 	// this state, we have found the leaf, so chop off the end 
26963 	// character to make it a leaf.
26964 	if (number.charAt(0) === "0") {
26965 		fields.trunkAccess = number.charAt(0);
26966 		fields.areaCode = number.substring(1, currentChar);
26967 	} else {
26968 		fields.areaCode = number.substring(0, currentChar);
26969 	}
26970 	this.processSubscriberNumber(number.substring(currentChar), fields, regionSettings);
26971 	
26972 	ret = {
26973 		number: ""
26974 	};
26975 	
26976 	return ret;
26977 };
26978 
26979 /**
26980  * @class
26981  * @private
26982  * @extends PhoneHandler
26983  * @constructor
26984  */
26985 var USStateHandler = function () {
26986 	return this;
26987 };
26988 
26989 USStateHandler.prototype = new PhoneHandler();
26990 USStateHandler.prototype.vsc = function (number, currentChar, fields, regionSettings) {
26991 	var ret;
26992 
26993 	// found a VSC code (ie. a "star code")
26994 	fields.vsc = number;
26995 
26996 	// treat the rest of the number as if it were a completely new number
26997 	ret = {
26998 		number: ""
26999 	};
27000 
27001 	return ret;
27002 };
27003 
27004 /**
27005  * Creates a phone handler instance that is correct for the locale. Phone handlers are
27006  * used to handle parsing of the various fields in a phone number.
27007  * 
27008  * @protected
27009  * @static
27010  * @return {PhoneHandler} the correct phone handler for the locale
27011  */
27012 var PhoneHandlerFactory = function (locale, plan) {
27013 	if (plan.getContextFree() !== undefined && typeof(plan.getContextFree()) === 'boolean' && plan.getContextFree() === false) {
27014 		return new CSStateHandler();
27015 	}
27016 	var region = (locale && locale.getRegion()) || "ZZ";
27017 	switch (region) {
27018 	case 'US':
27019 		return new USStateHandler();
27020 
27021 	default:
27022 		return new PhoneHandler();
27023 	}
27024 };
27025 
27026 
27027 /*< PhoneNumber.js */
27028 /*
27029  * phonenum.js - Represent a phone number.
27030  * 
27031  * Copyright © 2014-2015, JEDLSoft
27032  *
27033  * Licensed under the Apache License, Version 2.0 (the "License");
27034  * you may not use this file except in compliance with the License.
27035  * You may obtain a copy of the License at
27036  *
27037  *     http://www.apache.org/licenses/LICENSE-2.0
27038  *
27039  * Unless required by applicable law or agreed to in writing, software
27040  * distributed under the License is distributed on an "AS IS" BASIS,
27041  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
27042  *
27043  * See the License for the specific language governing permissions and
27044  * limitations under the License.
27045  */
27046 
27047 /*
27048 !depends 
27049 ilib.js
27050 NumberingPlan.js
27051 PhoneLocale.js
27052 PhoneHandlerFactory.js
27053 Utils.js
27054 JSUtils.js
27055 */
27056 
27057 // !data states idd mnc
27058 
27059 
27060 /**
27061  * @class
27062  * Create a new phone number instance that parses the phone number parameter for its 
27063  * constituent parts, and store them as separate fields in the returned object.
27064  * 
27065  * The options object may include any of these properties:
27066  * 
27067  * <ul>
27068  * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which
27069  * numbering plan to use.
27070  * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 
27071  * currently connected to, if known. This also can give a clue as to which numbering plan to
27072  * use
27073  * <li>onLoad - a callback function to call when this instance is fully 
27074  * loaded. When the onLoad option is given, this class will attempt to
27075  * load any missing locale data using the ilib loader callback.
27076  * When the constructor is done (even if the data is already preassembled), the 
27077  * onLoad function is called with the current instance as a parameter, so this
27078  * callback can be used with preassembled or dynamic loading or a mix of the two.
27079  * <li>sync - tell whether to load any missing locale data synchronously or 
27080  * asynchronously. If this option is given as "false", then the "onLoad"
27081  * callback must be given, as the instance returned from this constructor will
27082  * not be usable for a while.
27083  * <li><i>loadParams</i> - an object containing parameters to pass to the 
27084  * loader callback function when locale data is missing. The parameters are not
27085  * interpretted or modified in any way. They are simply passed along. The object 
27086  * may contain any property/value pairs as long as the calling code is in
27087  * agreement with the loader callback function as to what those parameters mean.
27088  * </ul>
27089  * 
27090  * This function is locale-sensitive, and will assume any number passed to it is
27091  * appropriate for the given locale. If the MCC is given, this method will assume
27092  * that numbers without an explicit country code have been dialled within the country
27093  * given by the MCC. This affects how things like area codes are parsed. If the MCC
27094  * is not given, this method will use the given locale to determine the country
27095  * code. If the locale is not explicitly given either, then this function uses the 
27096  * region of current locale as the default.<p>
27097  * 
27098  * The input number may contain any formatting characters for the given locale. Each 
27099  * field that is returned in the json object is a simple string of digits with
27100  * all formatting and whitespace characters removed.<p>
27101  * 
27102  * The number is decomposed into its parts, regardless if the number
27103  * contains formatting characters. If a particular part cannot be extracted from given 
27104  * number, the field will not be returned as a field in the object. If no fields can be
27105  * extracted from the number at all, then all digits found in the string will be 
27106  * returned in the subscriberNumber field. If the number parameter contains no 
27107  * digits, an empty object is returned.<p>
27108  * 
27109  * This instance can contain any of the following fields after parsing is done:
27110  * 
27111  * <ul>
27112  * <li>vsc - if this number starts with a VSC (Vertical Service Code, or "star code"), this field will contain the star and the code together
27113  * <li>iddPrefix - the prefix for international direct dialing. This can either be in the form of a plus character or the IDD access code for the given locale
27114  * <li>countryCode - if this number is an international direct dial number, this is the country code
27115  * <li>cic - for "dial-around" services (access to other carriers), this is the prefix used as the carrier identification code
27116  * <li>emergency - an emergency services number
27117  * <li>mobilePrefix - prefix that introduces a mobile phone number
27118  * <li>trunkAccess - trunk access code (long-distance access)
27119  * <li>serviceCode - like a geographic area code, but it is a required prefix for various services
27120  * <li>areaCode - geographic area codes
27121  * <li>subscriberNumber - the unique number of the person or company that pays for this phone line
27122  * <li>extension - in some countries, extensions are dialed directly without going through an operator or a voice prompt system. If the number includes an extension, it is given in this field.
27123  * <li>invalid - this property is added and set to true if the parser found that the number is invalid in the numbering plan for the country. This method will make its best effort at parsing, but any digits after the error will go into the subscriberNumber field
27124  * </ul>
27125  * 
27126  * The following rules determine how the number is parsed:
27127  * 
27128  * <ol>
27129  * <li>If the number starts with a character that is alphabetic instead of numeric, do
27130  * not parse the number at all. There is a good chance that it is not really a phone number.
27131  * In this case, an empty instance will be returned.
27132  * <li>If the phone number uses the plus notation or explicitly uses the international direct
27133  * dialing prefix for the given locale, then the country code is identified in 
27134  * the number. The rules of given locale are used to parse the IDD prefix, and then the rules
27135  * of the country in the prefix are used to parse the rest of the number.
27136  * <li>If a country code is provided as an argument to the function call, use that country's
27137  * parsing rules for the number. This is intended for programs like a Contacts application that 
27138  * know what the country is of the person that owns the phone number and can pass that on as 
27139  * a hint.
27140  * <li>If the appropriate locale cannot be easily determined, default to using the rules 
27141  * for the current user's region.
27142  * </ol>
27143  * 
27144  * Example: parsing the number "+49 02101345345-78" will give the following properties in the
27145  * resulting phone number instance:
27146  * 
27147  * <pre>
27148  *      {
27149  *        iddPrefix: "+",
27150  *        countryCode: "49",
27151  *        areaCode: "02101",
27152  *        subscriberNumber: "345345",
27153  *        extension: "78"
27154  *      }
27155  * </pre>
27156  *  
27157  * Note that in this example, because international direct dialing is explicitly used 
27158  * in the number, the part of this number after the IDD prefix and country code will be 
27159  * parsed exactly the same way in all locales with German rules (country code 49).
27160  *  
27161  * Regions currently supported are:
27162  *  
27163  * <ul>
27164  * <li>NANP (North American Numbering Plan) countries - USA, Canada, Bermuda, various Caribbean nations
27165  * <li>UK
27166  * <li>Republic of Ireland
27167  * <li>Germany
27168  * <li>France
27169  * <li>Spain
27170  * <li>Italy
27171  * <li>Mexico
27172  * <li>India
27173  * <li>People's Republic of China
27174  * <li>Netherlands
27175  * <li>Belgium
27176  * <li>Luxembourg
27177  * <li>Australia
27178  * <li>New Zealand
27179  * <li>Singapore
27180  * <li>Korea
27181  * <li>Japan
27182  * <li>Russia
27183  * <li>Brazil
27184  * </ul>
27185  * 
27186  * @constructor
27187  * @param {!string|PhoneNumber} number A free-form phone number to be parsed, or another phone
27188  * number instance to copy
27189  * @param {Object=} options options that guide the parser in parsing the number
27190  */
27191 var PhoneNumber = function(number, options) {
27192 	var stateData,
27193 		regionSettings;
27194 
27195 	this.sync = true;
27196 	this.loadParams = {};
27197 	
27198 
27199 	if (options) {
27200 		if (typeof(options.sync) === 'boolean') {
27201 			this.sync = options.sync;
27202 		}
27203 
27204 		if (options.loadParams) {
27205 			this.loadParams = options.loadParams;
27206 		}
27207 
27208 		if (typeof(options.onLoad) === 'function') {
27209 			/** 
27210 			 * @private
27211 			 * @type {function(PhoneNumber)} 
27212 			 */
27213 			this.onLoad = options.onLoad;
27214 		}
27215 	} else {
27216 	    options = {sync: true};
27217 	}
27218 
27219 	if (!number || (typeof number === "string" && number.length === 0)) {
27220         if (typeof(options.onLoad) === 'function') {
27221             options.onLoad(undefined);
27222         }
27223 
27224 	    return this;
27225 	}
27226 	
27227 	if (typeof number === "object") {
27228 		/**
27229 		 * The vertical service code. These are codes that typically
27230 		 * start with a star or hash, like "*69" for "dial back the 
27231 		 * last number that called me".
27232 		 * @type {string|undefined} 
27233 		 */
27234 		this.vsc = number.vsc;
27235 
27236 		/**
27237 		 * The international direct dialing prefix. This is always
27238 		 * followed by the country code. 
27239 		 * @type {string} 
27240 		 */
27241 		this.iddPrefix = number.iddPrefix;
27242 		
27243 		/**
27244 		 * The unique IDD country code for the country where the
27245 		 * phone number is serviced. 
27246 		 * @type {string|undefined} 
27247 		 */
27248 		this.countryCode = number.countryCode;
27249 		
27250 		/**
27251 		 * The digits required to access the trunk. 
27252 		 * @type {string|undefined} 
27253 		 */
27254 		this.trunkAccess = number.trunkAccess;
27255 		
27256 		/**
27257 		 * The carrier identification code used to identify 
27258 		 * alternate long distance or international carriers. 
27259 		 * @type {string|undefined} 
27260 		 */
27261 		this.cic = number.cic;
27262 		
27263 		/**
27264 		 * Identifies an emergency number that is typically
27265 		 * short, such as "911" in North America or "112" in
27266 		 * many other places in the world. 
27267 		 * @type {string|undefined} 
27268 		 */
27269 		this.emergency = number.emergency;
27270 		
27271 		/**
27272 		 * The prefix of the subscriber number that indicates
27273 		 * that this is the number of a mobile phone. 
27274 		 * @type {string|undefined} 
27275 		 */
27276 		this.mobilePrefix = number.mobilePrefix;
27277 		
27278 		/**
27279 		 * The prefix that identifies this number as commercial
27280 		 * service number. 
27281 		 * @type {string|undefined} 
27282 		 */
27283 		this.serviceCode = number.serviceCode;
27284 		
27285 		/**
27286 		 * The area code prefix of a land line number. 
27287 		 * @type {string|undefined} 
27288 		 */
27289 		this.areaCode = number.areaCode;
27290 		
27291 		/**
27292 		 * The unique number associated with the subscriber
27293 		 * of this phone. 
27294 		 * @type {string|undefined} 
27295 		 */
27296 		this.subscriberNumber = number.subscriberNumber;
27297 		
27298 		/**
27299 		 * The direct dial extension number. 
27300 		 * @type {string|undefined} 
27301 		 */
27302 		this.extension = number.extension;
27303 		
27304 		/**
27305 		 * @private
27306 		 * @type {boolean} 
27307 		 */
27308 		this.invalid = number.invalid;
27309 
27310 		if (number.plan && number.locale) {
27311 			/** 
27312 			 * @private
27313 			 * @type {NumberingPlan} 
27314 			 */
27315 			this.plan = number.plan;
27316 			
27317 			/** 
27318 			 * @private
27319 			 * @type {PhoneLocale} 
27320 			 */
27321 			this.locale = number.locale;
27322 	
27323 			/** 
27324 			 * @private
27325 			 * @type {NumberingPlan} 
27326 			 */
27327 			this.destinationPlan = number.destinationPlan;
27328 			
27329 			/** 
27330 			 * @private
27331 			 * @type {PhoneLocale} 
27332 			 */
27333 			this.destinationLocale = number.destinationLocale;
27334 	
27335 			if (options && typeof(options.onLoad) === 'function') {
27336 				options.onLoad(this);
27337 			}
27338 			return;
27339 		}
27340 	}
27341 
27342 	new PhoneLocale({
27343 		locale: options && options.locale,
27344 		mcc: options && options.mcc,
27345 		sync: this.sync,
27346 		loadParams: this.loadParams,
27347 		onLoad: ilib.bind(this, function(loc) {
27348 			this.locale = this.destinationLocale = loc;
27349 			new NumberingPlan({
27350 				locale: this.locale,
27351 				sync: this.sync,
27352 				loadParms: this.loadParams,
27353 				onLoad: ilib.bind(this, function (plan) {
27354 					this.plan = this.destinationPlan = plan;
27355 			
27356 					if (typeof number === "object") {
27357 						// the copy constructor code above did not find the locale 
27358 						// or plan before, but now they are loaded, so we can return 
27359 						// already without going further
27360 					    if (typeof(options.onLoad) === "function") {
27361 					        options.onLoad(this);
27362 					    }
27363 						return;
27364 					}
27365 					Utils.loadData({
27366 						name: "states.json",
27367 						object: "PhoneNumber",
27368 						locale: this.locale,
27369 						sync: this.sync,
27370 						loadParams: JSUtils.merge(this.loadParams, {
27371 							returnOne: true
27372 						}),
27373 						callback: ilib.bind(this, function (stdata) {
27374 							if (!stdata) {
27375 								stdata = PhoneNumber._defaultStates;
27376 							}
27377 		
27378 							stateData = stdata;
27379 
27380 							regionSettings = {
27381 								stateData: stateData,
27382 								plan: plan,
27383 								handler: PhoneHandlerFactory(this.locale, plan)
27384 							};
27385 							
27386 							// use ^ to indicate the beginning of the number, because certain things only match at the beginning
27387 							number = "^" + number.replace(/\^/g, '');
27388 							number = PhoneNumber._stripFormatting(number);
27389 
27390 							this._parseNumber(number, regionSettings, options);
27391 						})
27392 					});
27393 				})
27394 			});
27395 		})
27396 	});
27397 };
27398 
27399 /**
27400  * Parse an International Mobile Subscriber Identity (IMSI) number into its 3 constituent parts:
27401  * 
27402  * <ol>
27403  * <li>mcc - Mobile Country Code, which identifies the country where the phone is currently receiving 
27404  * service.
27405  * <li>mnc - Mobile Network Code, which identifies the carrier which is currently providing service to the phone 
27406  * <li>msin - Mobile Subscription Identifier Number. This is a unique number identifying the mobile phone on 
27407  * the network, which usually maps to an account/subscriber in the carrier's database.
27408  * </ol>
27409  * 
27410  * Because this function may need to load data to identify the above parts, you can pass an options
27411  * object that controls how the data is loaded. The options may contain any of the following properties:
27412  *
27413  * <ul>
27414  * <li>onLoad - a callback function to call when the parsing is done. When the onLoad option is given, 
27415  * this method will attempt to load the locale data using the ilib loader callback. When it is done
27416  * (even if the data is already preassembled), the onLoad function is called with the parsing results
27417  * as a parameter, so this callback can be used with preassembled or dynamic, synchronous or 
27418  * asynchronous loading or a mix of the above.
27419  * <li>sync - tell whether to load any missing locale data synchronously or asynchronously. If this 
27420  * option is given as "false", then the "onLoad" callback must be given, as the results returned from 
27421  * this constructor will not be usable for a while.
27422  * <li><i>loadParams</i> - an object containing parameters to pass to the loader callback function 
27423  * when locale data is missing. The parameters are not interpretted or modified in any way. They are 
27424  * simply passed along. The object may contain any property/value pairs as long as the calling code is in
27425  * agreement with the loader callback function as to what those parameters mean.
27426  * </ul>
27427  *
27428  * @static
27429  * @param {string} imsi IMSI number to parse
27430  * @param {Object} options options controlling the loading of the locale data
27431  * @return {{mcc:string,mnc:string,msin:string}|undefined} components of the IMSI number, when the locale data
27432  * is loaded synchronously, or undefined if asynchronous
27433  */
27434 PhoneNumber.parseImsi = function(imsi, options) {
27435 	var sync = true,
27436 		loadParams = {},
27437 		fields = {};
27438 	
27439 	if (!imsi) {
27440 	    if (options && typeof(options.onLoad) === 'function') {
27441             options.onLoad(undefined);
27442         }
27443 		return undefined;
27444 	}
27445 
27446 	if (options) {
27447 		if (typeof(options.sync) !== 'undefined') {
27448 			sync = !!options.sync;
27449 		}
27450 		
27451 		if (options.loadParams) {
27452 			loadParams = options.loadParams;
27453 		}
27454 	}	
27455 
27456 	if (ilib.data.mnc) {
27457 		fields = PhoneNumber._parseImsi(ilib.data.mnc, imsi);
27458 		
27459 		if (options && typeof(options.onLoad) === 'function') {
27460 			options.onLoad(fields);
27461 		}
27462 	} else {
27463 		Utils.loadData({
27464 			name: "mnc.json", 
27465 			object: "PhoneNumber", 
27466 			nonlocale: true, 
27467 			sync: sync, 
27468 			loadParams: loadParams, 
27469 			callback: ilib.bind(this, function(data) {
27470 				ilib.data.mnc = data;
27471 				fields = PhoneNumber._parseImsi(data, imsi);
27472 				
27473 				if (options && typeof(options.onLoad) === 'function') {
27474 					options.onLoad(fields);
27475 				}
27476 			})
27477 		});
27478 	}
27479 	return fields;
27480 };
27481 
27482 
27483 /**
27484  * @static
27485  * @protected
27486  */
27487 PhoneNumber._parseImsi = function(data, imsi) {
27488 	var ch, 
27489 		i,
27490 		currentState, 
27491 		end, 
27492 		handlerMethod,
27493 		newState,
27494 		fields = {},
27495 		lastLeaf,
27496 		consumed = 0;
27497 	
27498 	currentState = data;
27499 	if (!currentState) {
27500 		// can't parse anything
27501 		return undefined;
27502 	}
27503 	
27504 	i = 0;
27505 	while (i < imsi.length) {
27506 		ch = PhoneNumber._getCharacterCode(imsi.charAt(i));
27507 		// console.info("parsing char " + imsi.charAt(i) + " code: " + ch);
27508 		if (ch >= 0) {
27509 			newState = currentState.s && currentState.s[ch];
27510 			
27511 			if (typeof(newState) === 'object') {
27512 				if (typeof(newState.l) !== 'undefined') {
27513 					// save for latter if needed
27514 					lastLeaf = newState;
27515 					consumed = i;
27516 				}
27517 				// console.info("recognized digit " + ch + " continuing...");
27518 				// recognized digit, so continue parsing
27519 				currentState = newState;
27520 				i++;
27521 			} else {
27522 				if ((typeof(newState) === 'undefined' || newState === 0 ||
27523 					(typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) &&
27524 					 lastLeaf) {
27525 					// this is possibly a look-ahead and it didn't work... 
27526 					// so fall back to the last leaf and use that as the
27527 					// final state
27528 					newState = lastLeaf;
27529 					i = consumed;
27530 				}
27531 				
27532 				if ((typeof(newState) === 'number' && newState) ||
27533 					(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
27534 					// final state
27535 					var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
27536 					handlerMethod = PhoneNumber._states[stateNumber];
27537 
27538 					// console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i);
27539 	
27540 					// deal with syntactic ambiguity by using the "special" end state instead of "area"
27541 					if ( handlerMethod === "area" ) {
27542 						end = i+1;
27543 					} else {
27544 						// unrecognized imsi, so just assume the mnc is 3 digits
27545 						end = 6;
27546 					}
27547 					
27548 					fields.mcc = imsi.substring(0,3);
27549 					fields.mnc = imsi.substring(3,end);
27550 					fields.msin = imsi.substring(end);
27551 	
27552 					return fields;
27553 				} else {
27554 					// parse error
27555 					if (imsi.length >= 6) {
27556 						fields.mcc = imsi.substring(0,3);
27557 						fields.mnc = imsi.substring(3,6);
27558 						fields.msin = imsi.substring(6);
27559 					}
27560 					return fields;
27561 				}
27562 			}
27563 		} else if (ch === -1) {
27564 			// non-transition character, continue parsing in the same state
27565 			i++;
27566 		} else {
27567 			// should not happen
27568 			// console.info("skipping character " + ch);
27569 			// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
27570 			i++;
27571 		}
27572 	}
27573 		
27574 	if (i >= imsi.length && imsi.length >= 6) {
27575 		// we reached the end of the imsi, but did not finish recognizing anything. 
27576 		// Default to last resort and assume 3 digit mnc
27577 		fields.mcc = imsi.substring(0,3);
27578 		fields.mnc = imsi.substring(3,6);
27579 		fields.msin = imsi.substring(6);
27580 	} else {
27581 		// unknown or not enough characters for a real imsi 
27582 		fields = undefined;
27583 	}
27584 		
27585 	// console.info("Globalization.Phone.parseImsi: final result is: " + JSON.stringify(fields));
27586 	return fields;
27587 };
27588 
27589 /**
27590  * @static
27591  * @private
27592  */
27593 PhoneNumber._stripFormatting = function(str) {
27594 	var ret = "";
27595 	var i;
27596 
27597 	for (i = 0; i < str.length; i++) {
27598 		if (PhoneNumber._getCharacterCode(str.charAt(i)) >= -1) {
27599 			ret += str.charAt(i);
27600 		}
27601 	}
27602 	return ret;
27603 };
27604 
27605 /**
27606  * @static
27607  * @protected
27608  */
27609 PhoneNumber._getCharacterCode = function(ch) {
27610 	if (ch >= '0' && ch <= '9') {
27611 			return ch - '0';
27612 		}
27613 	switch (ch) {
27614 	case '+':
27615 		return 10;
27616 	case '*':
27617 		return 11;
27618 	case '#':
27619 		return 12;
27620 	case '^':
27621 		return 13;
27622 	case 'p':		// pause chars
27623 	case 'P':
27624 	case 't':
27625 	case 'T':
27626 	case 'w':
27627 	case 'W':
27628 		return -1;
27629 	case 'x':
27630 	case 'X':
27631 	case ',':
27632 	case ';':		// extension char
27633 		return -1;
27634 	}
27635 	return -2;
27636 };
27637 
27638 /**
27639  * @private
27640  */
27641 PhoneNumber._states = [
27642 	"none",
27643 	"unknown",
27644 	"plus",
27645 	"idd",
27646 	"cic",
27647 	"service",
27648 	"cell",
27649 	"area",
27650 	"vsc",
27651 	"country",
27652 	"personal",
27653 	"special",
27654 	"trunk",
27655 	"premium",
27656 	"emergency",
27657 	"service2",
27658 	"service3",
27659 	"service4",
27660 	"cic2",
27661 	"cic3",
27662 	"start",
27663 	"local"
27664 ];
27665 
27666 /**
27667  * @private
27668  */
27669 PhoneNumber._fieldOrder = [
27670 	"vsc",
27671 	"iddPrefix",
27672 	"countryCode",
27673 	"trunkAccess",
27674 	"cic",
27675 	"emergency",
27676 	"mobilePrefix",
27677 	"serviceCode",
27678 	"areaCode",
27679 	"subscriberNumber",
27680 	"extension"
27681 ];
27682 
27683 PhoneNumber._defaultStates = {
27684 	"s": [
27685         0,
27686 		21,  // 1 -> local
27687         21,  // 2 -> local
27688         21,  // 3 -> local
27689         21,  // 4 -> local
27690         21,  // 5 -> local
27691         21,  // 6 -> local
27692         21,  // 7 -> local
27693         21,  // 8 -> local
27694         21,  // 9 -> local
27695         0,0,0,
27696 	    { // ^
27697 	    	"s": [
27698 				{ // ^0
27699 					"s": [3], // ^00 -> idd
27700 					"l": 12   // ^0  -> trunk
27701 				},
27702 				21,  // ^1 -> local
27703 	            21,  // ^2 -> local
27704 	            21,  // ^3 -> local
27705 	            21,  // ^4 -> local
27706 	            21,  // ^5 -> local
27707 	            21,  // ^6 -> local
27708 	            21,  // ^7 -> local
27709 	            21,  // ^8 -> local
27710 	            21,  // ^9 -> local
27711 	            2    // ^+ -> plus
27712 	        ]
27713 	    }
27714 	]
27715 };
27716 
27717 PhoneNumber.prototype = {
27718 	/**
27719 	 * @protected
27720 	 * @param {string} number
27721 	 * @param {Object} regionData
27722 	 * @param {Object} options
27723 	 * @param {string} countryCode
27724 	 */
27725 	_parseOtherCountry: function(number, regionData, options, countryCode) {
27726 		new PhoneLocale({
27727 			locale: this.locale,
27728 			countryCode: countryCode,
27729 			sync: this.sync,
27730 			loadParms: this.loadParams,
27731 			onLoad: ilib.bind(this, function (loc) {
27732 				/*
27733 				 * this.locale is the locale where this number is being parsed,
27734 				 * and is used to parse the IDD prefix, if any, and this.destinationLocale is 
27735 				 * the locale of the rest of this number after the IDD prefix.
27736 				 */
27737 				/** @type {PhoneLocale} */
27738 				this.destinationLocale = loc;
27739 
27740 				Utils.loadData({
27741 					name: "states.json",
27742 					object: "PhoneNumber",
27743 					locale: this.destinationLocale,
27744 					sync: this.sync,
27745 					loadParams: JSUtils.merge(this.loadParams, {
27746 						returnOne: true
27747 					}),
27748 					callback: ilib.bind(this, function (stateData) {
27749 						if (!stateData) {
27750 							stateData = PhoneNumber._defaultStates;
27751 						}
27752 						
27753 						new NumberingPlan({
27754 							locale: this.destinationLocale,
27755 							sync: this.sync,
27756 							loadParms: this.loadParams,
27757 							onLoad: ilib.bind(this, function (plan) {
27758 								/*
27759 								 * this.plan is the plan where this number is being parsed,
27760 								 * and is used to parse the IDD prefix, if any, and this.destinationPlan is 
27761 								 * the plan of the rest of this number after the IDD prefix in the 
27762 								 * destination locale.
27763 								 */
27764 								/** @type {NumberingPlan} */
27765 								this.destinationPlan = plan;
27766 
27767 								var regionSettings = {
27768 									stateData: stateData,
27769 									plan: plan,
27770 									handler: PhoneHandlerFactory(this.destinationLocale, plan)
27771 								};
27772 								
27773 								// for plans that do not skip the trunk code when dialing from
27774 								// abroad, we need to treat the number from here on in as if it 
27775 								// were parsing a local number from scratch. That way, the parser
27776 								// does not get confused between parts of the number at the
27777 								// beginning of the number, and parts in the middle.
27778 								if (!plan.getSkipTrunk()) {
27779 									number = '^' + number;
27780 								}
27781 									
27782 								// recursively call the parser with the new states data
27783 								// to finish the parsing
27784 								this._parseNumber(number, regionSettings, options);
27785 							})
27786 						});
27787 					})
27788 				});
27789 			})
27790 		});
27791 	},
27792 	
27793 	/**
27794 	 * @protected
27795 	 * @param {string} number
27796 	 * @param {Object} regionData
27797 	 * @param {Object} options
27798 	 */
27799 	_parseNumber: function(number, regionData, options) {
27800 		var i, ch,
27801 			regionSettings,
27802 			newState,
27803 			dot,
27804 			handlerMethod,
27805 			result,
27806 			currentState = regionData.stateData,
27807 			lastLeaf = undefined,
27808 			consumed = 0;
27809 
27810 		regionSettings = regionData;
27811 		dot = 14; // special transition which matches all characters. See AreaCodeTableMaker.java
27812 
27813 		i = 0;
27814 		while (i < number.length) {
27815 			ch = PhoneNumber._getCharacterCode(number.charAt(i));
27816 			if (ch >= 0) {
27817 				// newState = stateData.states[state][ch];
27818 				newState = currentState.s && currentState.s[ch];
27819 				
27820 				if (!newState && currentState.s && currentState.s[dot]) {
27821 					newState = currentState.s[dot];
27822 				}
27823 				
27824 				if (typeof(newState) === 'object' && i+1 < number.length) {
27825 					if (typeof(newState.l) !== 'undefined') {
27826 						// this is a leaf node, so save that for later if needed
27827 						lastLeaf = newState;
27828 						consumed = i;
27829 					}
27830 					// console.info("recognized digit " + ch + " continuing...");
27831 					// recognized digit, so continue parsing
27832 					currentState = newState;
27833 					i++;
27834 				} else {
27835 					if ((typeof(newState) === 'undefined' || newState === 0 ||
27836 						(typeof(newState) === 'object' && typeof(newState.l) === 'undefined')) &&
27837 						 lastLeaf) {
27838 						// this is possibly a look-ahead and it didn't work... 
27839 						// so fall back to the last leaf and use that as the
27840 						// final state
27841 						newState = lastLeaf;
27842 						i = consumed;
27843 						consumed = 0;
27844 						lastLeaf = undefined;
27845 					}
27846 					
27847 					if ((typeof(newState) === 'number' && newState) ||
27848 						(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
27849 						// final state
27850 						var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
27851 						handlerMethod = PhoneNumber._states[stateNumber];
27852 						
27853 						if (number.charAt(0) === '^') {
27854 							result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
27855 						} else {
27856 							result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
27857 						}
27858 		
27859 						// reparse whatever is left
27860 						number = result.number;
27861 						i = consumed = 0;
27862 						lastLeaf = undefined;
27863 						
27864 						//console.log("reparsing with new number: " +  number);
27865 						currentState = regionSettings.stateData;
27866 						// if the handler requested a special sub-table, use it for this round of parsing,
27867 						// otherwise, set it back to the regular table to continue parsing
27868 	
27869 						if (result.countryCode !== undefined) {
27870 							this._parseOtherCountry(number, regionData, options, result.countryCode);
27871 							// don't process any further -- let the work be done in the onLoad callbacks
27872 							return;
27873 						} else if (result.table !== undefined) {
27874 							Utils.loadData({
27875 								name: result.table + ".json",
27876 								object: "PhoneNumber",
27877 								nonlocale: true,
27878 								sync: this.sync,
27879 								loadParams: this.loadParams,
27880 								callback: ilib.bind(this, function (data) {
27881 									if (!data) {
27882 										data = PhoneNumber._defaultStates;
27883 									}
27884 	
27885 									regionSettings = {
27886 										stateData: data,
27887 										plan: regionSettings.plan,
27888 										handler: regionSettings.handler
27889 									};
27890 									
27891 									// recursively call the parser with the new states data
27892 									// to finish the parsing
27893 									this._parseNumber(number, regionSettings, options);
27894 								})
27895 							});
27896 							// don't process any further -- let the work be done in the onLoad callbacks
27897 							return;
27898 						} else if (result.skipTrunk !== undefined) {
27899 							ch = PhoneNumber._getCharacterCode(regionSettings.plan.getTrunkCode());
27900 							currentState = currentState.s && currentState.s[ch];
27901 						}
27902 					} else {
27903 						handlerMethod = (typeof(newState) === 'number') ? "none" : "local";
27904 						// failed parse. Either no last leaf to fall back to, or there was an explicit
27905 						// zero in the table. Put everything else in the subscriberNumber field as the
27906 						// default place
27907 						if (number.charAt(0) === '^') {
27908 							result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
27909 						} else {
27910 							result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
27911 						}
27912 						break;
27913 					}
27914 				}
27915 			} else if (ch === -1) {
27916 				// non-transition character, continue parsing in the same state
27917 				i++;
27918 			} else {
27919 				// should not happen
27920 				// console.info("skipping character " + ch);
27921 				// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
27922 				i++;
27923 			}
27924 		}
27925 		if (i >= number.length && currentState !== regionData.stateData) {
27926 			handlerMethod = (typeof(currentState.l) === 'undefined' || currentState === 0) ? "none" : "local";
27927 			// we reached the end of the phone number, but did not finish recognizing anything. 
27928 			// Default to last resort and put everything that is left into the subscriber number
27929 			//console.log("Reached end of number before parsing was complete. Using handler for method none.")
27930 			if (number.charAt(0) === '^') {
27931 				result = regionSettings.handler[handlerMethod](number.slice(1), i-1, this, regionSettings);
27932 			} else {
27933 				result = regionSettings.handler[handlerMethod](number, i, this, regionSettings);
27934 			}
27935 		}
27936 
27937 		// let the caller know we are done parsing
27938 		if (this.onLoad) {
27939 			this.onLoad(this);
27940 		}
27941 	},
27942 	/**
27943 	 * @protected
27944 	 */
27945 	_getPrefix: function() {
27946 		return this.areaCode || this.serviceCode || this.mobilePrefix || "";
27947 	},
27948 	
27949 	/**
27950 	 * @protected
27951 	 */
27952 	_hasPrefix: function() {
27953 		return (this._getPrefix() !== "");
27954 	},
27955 	
27956 	/**
27957 	 * Exclusive or -- return true, if one is defined and the other isn't
27958 	 * @protected
27959 	 */
27960 	_xor : function(left, right) {
27961 		if ((left === undefined && right === undefined ) || (left !== undefined && right !== undefined)) {
27962 			return false;
27963 		} else {
27964 			return true;
27965 		}
27966 	},
27967 	
27968 	/**
27969 	 * return a version of the phone number that contains only the dialable digits in the correct order 
27970 	 * @protected
27971 	 */
27972 	_join: function () {
27973 		var fieldName, formatted = "";
27974 		
27975 		try {
27976 			for (var field in PhoneNumber._fieldOrder) {
27977 				if (typeof field === 'string' && typeof PhoneNumber._fieldOrder[field] === 'string') {
27978 					fieldName = PhoneNumber._fieldOrder[field];
27979 					// console.info("normalize: formatting field " + fieldName);
27980 					if (this[fieldName] !== undefined) {
27981 						formatted += this[fieldName];
27982 					}
27983 				}
27984 			}
27985 		} catch ( e ) {
27986 			//console.warn("caught exception in _join: " + e);
27987 			throw e;
27988 		}
27989 		return formatted;
27990 	},
27991 
27992 	/**
27993 	 * This routine will compare the two phone numbers in an locale-sensitive
27994 	 * manner to see if they possibly reference the same phone number.<p>
27995 	 * 
27996 	 * In many places,
27997 	 * there are multiple ways to reach the same phone number. In North America for 
27998 	 * example, you might have a number with the trunk access code of "1" and another
27999 	 * without, and they reference the exact same phone number. This is considered a
28000 	 * strong match. For a different pair of numbers, one may be a local number and
28001 	 * the other a full phone number with area code, which may reference the same 
28002 	 * phone number if the local number happens to be located in that area code. 
28003 	 * However, you cannot say for sure if it is in that area code, so it will 
28004 	 * be considered a somewhat weaker match.<p>
28005 	 *  
28006 	 * Similarly, in other countries, there are sometimes different ways of 
28007 	 * reaching the same destination, and the way that numbers
28008 	 * match depends on the locale.<p>
28009 	 * 
28010 	 * The various phone number fields are handled differently for matches. There
28011 	 * are various fields that do not need to match at all. For example, you may
28012 	 * type equally enter "00" or "+" into your phone to start international direct
28013 	 * dialling, so the iddPrefix field does not need to match at all.<p> 
28014 	 * 
28015 	 * Typically, fields that require matches need to match exactly if both sides have a value 
28016 	 * for that field. If both sides specify a value and those values differ, that is
28017 	 * a strong non-match. If one side does not have a value and the other does, that 
28018 	 * causes a partial match, because the number with the missing field may possibly
28019 	 * have an implied value that matches the other number. For example, the numbers
28020 	 * "650-555-1234" and "555-1234" have a partial match as the local number "555-1234"
28021 	 * might possibly have the same 650 area code as the first number, and might possibly
28022 	 * not. If both side do not specify a value for a particular field, that field is 
28023 	 * considered matching.<p>
28024 	 *  
28025 	 * The values of following fields are ignored when performing matches:
28026 	 * 
28027 	 * <ul>
28028 	 * <li>vsc
28029 	 * <li>iddPrefix
28030 	 * <li>cic
28031 	 * <li>trunkAccess
28032 	 * </ul>
28033 	 * 
28034 	 * The values of the following fields matter if they do not match:
28035 	 *   
28036 	 * <ul>
28037 	 * <li>countryCode - A difference causes a moderately strong problem except for 
28038 	 * certain countries where there is a way to access the same subscriber via IDD 
28039 	 * and via intranetwork dialling
28040 	 * <li>mobilePrefix - A difference causes a possible non-match
28041 	 * <li>serviceCode - A difference causes a possible non-match
28042 	 * <li>areaCode - A difference causes a possible non-match
28043 	 * <li>subscriberNumber - A difference causes a very strong non-match
28044 	 * <li>extension - A difference causes a minor non-match
28045 	 * </ul>
28046 	 *  
28047 	 * @param {string|PhoneNumber} other other phone number to compare this one to
28048 	 * @return {number} non-negative integer describing the percentage quality of the 
28049 	 * match. 100 means a very strong match (100%), and lower numbers are less and 
28050 	 * less strong, down to 0 meaning not at all a match. 
28051 	 */
28052 	compare: function (other) {
28053 		var match = 100,
28054 			FRdepartments = {"590":1, "594":1, "596":1, "262":1},
28055 			ITcountries = {"378":1, "379":1},
28056 			thisPrefix,
28057 			otherPrefix,
28058 			currentCountryCode = 0;
28059 
28060 		if (typeof this.locale.region === "string") {
28061 			currentCountryCode = this.locale._mapRegiontoCC(this.locale.region);
28062 		}
28063 		
28064 		// subscriber number must be present and must match
28065 		if (!this.subscriberNumber || !other.subscriberNumber || this.subscriberNumber !== other.subscriberNumber) {
28066 			return 0;
28067 		}
28068 
28069 		// extension must match if it is present
28070 		if (this._xor(this.extension, other.extension) || this.extension !== other.extension) {
28071 			return 0;
28072 		}
28073 
28074 		if (this._xor(this.countryCode, other.countryCode)) {
28075 			// if one doesn't have a country code, give it some demerit points, but if the
28076 			// one that has the country code has something other than the current country
28077 			// add even more. Ignore the special cases where you can dial the same number internationally or via 
28078 			// the local numbering system
28079 			switch (this.locale.getRegion()) {
28080 			case 'FR':
28081 				if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) {
28082 					if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) {
28083 						match -= 100;
28084 					}
28085 				} else {
28086 					match -= 16;
28087 				}
28088 				break;
28089 			case 'IT':
28090 				if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 
28091 					if (this.areaCode !== other.areaCode) {
28092 						match -= 100;
28093 					}
28094 				} else {
28095 					match -= 16;
28096 				}
28097 				break;
28098 			default:
28099 				match -= 16;
28100 				if ((this.countryCode !== undefined && this.countryCode !== currentCountryCode) || 
28101 					(other.countryCode !== undefined && other.countryCode !== currentCountryCode)) {
28102 					match -= 16;
28103 				}
28104 			}
28105 		} else if (this.countryCode !== other.countryCode) {
28106 			// ignore the special cases where you can dial the same number internationally or via 
28107 			// the local numbering system
28108 			if (other.countryCode === '33' || this.countryCode === '33') {
28109 				// france
28110 				if (this.countryCode in FRdepartments || other.countryCode in FRdepartments) {
28111 					if (this.areaCode !== other.areaCode || this.mobilePrefix !== other.mobilePrefix) {
28112 						match -= 100;
28113 					}
28114 				} else {
28115 					match -= 100;
28116 				}
28117 			} else if (this.countryCode === '39' || other.countryCode === '39') {
28118 				// italy
28119 				if (this.countryCode in ITcountries || other.countryCode in ITcountries) { 
28120 					if (this.areaCode !== other.areaCode) {
28121 						match -= 100;
28122 					}
28123 				} else {
28124 					match -= 100;
28125 				}
28126 			} else {
28127 				match -= 100;
28128 			}
28129 		}
28130 
28131 		if (this._xor(this.serviceCode, other.serviceCode)) {
28132 			match -= 20;
28133 		} else if (this.serviceCode !== other.serviceCode) {
28134 			match -= 100;
28135 		}
28136 
28137 		if (this._xor(this.mobilePrefix, other.mobilePrefix)) {
28138 			match -= 20;
28139 		} else if (this.mobilePrefix !== other.mobilePrefix) {
28140 			match -= 100;
28141 		}
28142 
28143 		if (this._xor(this.areaCode, other.areaCode)) {
28144 			// one has an area code, the other doesn't, so dock some points. It could be a match if the local
28145 			// number in the one number has the same implied area code as the explicit area code in the other number.
28146 			match -= 12;
28147 		} else if (this.areaCode !== other.areaCode) {
28148 			match -= 100;
28149 		}
28150 
28151 		thisPrefix = this._getPrefix();
28152 		otherPrefix = other._getPrefix();
28153 		
28154 		if (thisPrefix && otherPrefix && thisPrefix !== otherPrefix) {
28155 			match -= 100;
28156 		}
28157 		
28158 		// make sure we are between 0 and 100
28159 		if (match < 0) {
28160 			match = 0;	
28161 		} else if (match > 100) {
28162 			match = 100;
28163 		}
28164 
28165 		return match;
28166 	},
28167 	
28168 	/**
28169 	 * Determine whether or not the other phone number is exactly equal to the current one.<p>
28170 	 *  
28171 	 * The difference between the compare method and the equals method is that the compare 
28172 	 * method compares normalized numbers with each other and returns the degree of match,
28173 	 * whereas the equals operator returns true iff the two numbers contain the same fields
28174 	 * and the fields are exactly the same. Functions and other non-phone number properties
28175 	 * are not compared.
28176 	 * @param {string|PhoneNumber} other another phone number to compare to this one
28177 	 * @return {boolean} true if the numbers are the same, false otherwise
28178 	 */
28179 	equals: function equals(other) {
28180 		if (other.locale && this.locale && !this.locale.equals(other.locale) && (!this.countryCode || !other.countryCode)) {
28181 			return false;
28182 		}
28183 
28184 		var _this = this;
28185 		return PhoneNumber._fieldOrder.every(function(field) {
28186 		    return _this[field] === other[field];
28187 		});
28188 	},
28189 
28190 	/**
28191 	 * @private
28192 	 * @param {{
28193 	 *   mcc:string,
28194 	 *   defaultAreaCode:string,
28195 	 *   country:string,
28196 	 *   networkType:string,
28197 	 *   assistedDialing:boolean,
28198 	 *   sms:boolean,
28199 	 *   manualDialing:boolean
28200 	 * }} options an object containing options to help in normalizing. 
28201 	 * @param {PhoneNumber} norm
28202 	 * @param {PhoneLocale} homeLocale
28203 	 * @param {PhoneLocale} currentLocale
28204 	 * @param {NumberingPlan} currentPlan
28205 	 * @param {PhoneLocale} destinationLocale
28206 	 * @param {NumberingPlan} destinationPlan
28207 	 * @param {boolean} sync
28208 	 * @param {Object|undefined} loadParams
28209 	 */
28210 	_doNormalize: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams) {
28211 		var formatted = "";
28212 		
28213 		if (!norm.invalid && options && options.assistedDialing) {
28214 			// don't normalize things that don't have subscriber numbers. Also, don't normalize
28215 			// manually dialed local numbers. Do normalize local numbers in contact entries.
28216 			if (norm.subscriberNumber && 
28217 					(!options.manualDialing ||
28218 					 norm.iddPrefix ||
28219 					 norm.countryCode ||
28220 					 norm.trunkAccess)) {
28221 				// console.log("normalize: assisted dialling normalization of " + JSON.stringify(norm));
28222 				if (currentLocale.getRegion() !== destinationLocale.getRegion()) {
28223 					// we are currently calling internationally
28224 					if (!norm._hasPrefix() && 
28225 							options.defaultAreaCode && 
28226 							destinationLocale.getRegion() === homeLocale.getRegion() &&
28227 							(!destinationPlan.getFieldLength("minLocalLength") || 
28228 								norm.subscriberNumber.length >= destinationPlan.getFieldLength("minLocalLength"))) {
28229 						// area code is required when dialling from international, but only add it if we are dialing
28230 						// to our home area. Otherwise, the default area code is not valid!
28231 						norm.areaCode = options.defaultAreaCode;
28232 						if (!destinationPlan.getSkipTrunk() && destinationPlan.getTrunkCode()) {
28233 							// some phone systems require the trunk access code, even when dialling from international
28234 							norm.trunkAccess = destinationPlan.getTrunkCode();
28235 						}
28236 					}
28237 					
28238 					if (norm.trunkAccess && destinationPlan.getSkipTrunk()) {
28239 						// on some phone systems, the trunk access code is dropped when dialling from international
28240 						delete norm.trunkAccess;
28241 					}
28242 					
28243 					// make sure to get the country code for the destination region, not the current region!
28244 					if (options.sms) {
28245 						if (homeLocale.getRegion() === "US" && currentLocale.getRegion() !== "US") {
28246 							if (destinationLocale.getRegion() !== "US") {
28247 								norm.iddPrefix = "011"; // non-standard code to make it go through the US first
28248 								norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.getRegion());
28249 							} else if (options.networkType === "cdma") {
28250 								delete norm.iddPrefix;
28251 								delete norm.countryCode;
28252 								if (norm.areaCode) {
28253 									norm.trunkAccess = "1";
28254 								}
28255 							} else if (norm.areaCode) {
28256 								norm.iddPrefix = "+";
28257 								norm.countryCode = "1";
28258 								delete norm.trunkAccess;
28259 							}
28260 						} else {
28261 							norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+";
28262 							norm.countryCode = norm.countryCode || homeLocale._mapRegiontoCC(destinationLocale.region);
28263 						}
28264 					} else if (norm._hasPrefix() && !norm.countryCode) {
28265 						norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.region);
28266 					}
28267 
28268 					if (norm.countryCode && !options.sms) {
28269 						// for CDMA, make sure to get the international dialling access code for the current region, not the destination region
28270 						// all umts carriers support plus dialing
28271 						norm.iddPrefix = (options.networkType === "cdma") ? currentPlan.getIDDCode() : "+";
28272 					}
28273 				} else {
28274 					// console.log("normalize: dialing within the country");
28275 					if (options.defaultAreaCode) {
28276 						if (destinationPlan.getPlanStyle() === "open") {
28277 							if (!norm.trunkAccess && norm._hasPrefix() && destinationPlan.getTrunkCode()) {
28278 								// call is not local to this area code, so you have to dial the trunk code and the area code
28279 								norm.trunkAccess = destinationPlan.getTrunkCode();
28280 							}
28281 						} else {
28282 							// In closed plans, you always have to dial the area code, even if the call is local.
28283 							if (!norm._hasPrefix()) {
28284 								if (destinationLocale.getRegion() === homeLocale.getRegion()) {
28285 									norm.areaCode = options.defaultAreaCode;
28286 									if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) {
28287 										norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode();
28288 									}
28289 								}
28290 							} else {
28291 								if (destinationPlan.getTrunkRequired() && destinationPlan.getTrunkCode()) {
28292 									norm.trunkAccess = norm.trunkAccess || destinationPlan.getTrunkCode();
28293 								}
28294 							}
28295 						}
28296 					}
28297 					
28298 					if (options.sms &&
28299 							homeLocale.getRegion() === "US" && 
28300 							currentLocale.getRegion() !== "US") {
28301 						norm.iddPrefix = "011"; // make it go through the US first
28302 						if (destinationPlan.getSkipTrunk() && norm.trunkAccess) {
28303 							delete norm.trunkAccess;
28304 						}
28305 					} else if (norm.iddPrefix || norm.countryCode) {
28306 						// we are in our destination country, so strip the international dialling prefixes
28307 						delete norm.iddPrefix;
28308 						delete norm.countryCode;
28309 						
28310 						if ((destinationPlan.getPlanStyle() === "open" || destinationPlan.getTrunkRequired()) && destinationPlan.getTrunkCode()) {
28311 							norm.trunkAccess = destinationPlan.getTrunkCode();
28312 						}
28313 					}
28314 				}
28315 			}
28316 		} else if (!norm.invalid) {
28317 			// console.log("normalize: non-assisted normalization");
28318 			if (!norm._hasPrefix() && options && options.defaultAreaCode && destinationLocale.getRegion() === homeLocale.region) {
28319 				norm.areaCode = options.defaultAreaCode;
28320 			}
28321 			
28322 			if (!norm.countryCode && norm._hasPrefix()) {
28323 				norm.countryCode = homeLocale._mapRegiontoCC(destinationLocale.getRegion());
28324 			}
28325 
28326 			if (norm.countryCode) {
28327 				if (options && options.networkType && options.networkType === "cdma") {
28328 					norm.iddPrefix = currentPlan.getIDDCode(); 
28329 				} else {
28330 					// all umts carriers support plus dialing
28331 					norm.iddPrefix = "+";
28332 				}
28333 		
28334 				if (destinationPlan.getSkipTrunk() && norm.trunkAccess) {
28335 					delete norm.trunkAccess;
28336 				} else if (!destinationPlan.getSkipTrunk() && !norm.trunkAccess && destinationPlan.getTrunkCode()) {
28337 					norm.trunkAccess = destinationPlan.getTrunkCode();
28338 				}
28339 			}
28340 		}
28341 		
28342 		// console.info("normalize: after normalization, the normalized phone number is: " + JSON.stringify(norm));
28343 		formatted = norm._join();
28344 
28345 		return formatted;
28346 	},
28347 	
28348 	/**
28349 	 * @private
28350 	 * @param {{
28351 	 *   mcc:string,
28352 	 *   defaultAreaCode:string,
28353 	 *   country:string,
28354 	 *   networkType:string,
28355 	 *   assistedDialing:boolean,
28356 	 *   sms:boolean,
28357 	 *   manualDialing:boolean
28358 	 * }} options an object containing options to help in normalizing. 
28359 	 * @param {PhoneNumber} norm
28360 	 * @param {PhoneLocale} homeLocale
28361 	 * @param {PhoneLocale} currentLocale
28362 	 * @param {NumberingPlan} currentPlan
28363 	 * @param {PhoneLocale} destinationLocale
28364 	 * @param {NumberingPlan} destinationPlan
28365 	 * @param {boolean} sync
28366 	 * @param {Object|undefined} loadParams
28367 	 * @param {function(string)} callback
28368 	 */
28369 	_doReparse: function(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams, callback) {
28370 		var formatted, 
28371 			tempRegion;
28372 		
28373 		if (options &&
28374 				options.assistedDialing &&
28375 				!norm.trunkAccess && 
28376 				!norm.iddPrefix &&
28377 				norm.subscriberNumber && 
28378 				norm.subscriberNumber.length > destinationPlan.getFieldLength("maxLocalLength")) {
28379 
28380 			// numbers that are too long are sometimes international direct dialed numbers that
28381 			// are missing the IDD prefix. So, try reparsing it using a plus in front to see if that works.
28382 			new PhoneNumber("+" + this._join(), {
28383 				locale: this.locale,
28384 				sync: sync,
28385 				loadParms: loadParams,
28386 				onLoad: ilib.bind(this, function (data) {
28387 					tempRegion = (data.countryCode && data.locale._mapCCtoRegion(data.countryCode));
28388 
28389 					if (tempRegion && tempRegion !== "unknown" && tempRegion !== "SG") {
28390 						// only use it if it is a recognized country code. Singapore (SG) is a special case.
28391 						norm = data;
28392 						destinationLocale = data.destinationLocale;
28393 						destinationPlan = data.destinationPlan;
28394 					}
28395 					
28396 					formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
28397 					if (typeof(callback) === 'function') {
28398 						callback(formatted);
28399 					}
28400 				})
28401 			});
28402 		} else if (options && options.assistedDialing && norm.invalid && currentLocale.region !== norm.locale.region) {
28403 			// if this number is not valid for the locale it was parsed with, try again with the current locale
28404 			// console.log("norm is invalid. Attempting to reparse with the current locale");
28405 
28406 			new PhoneNumber(this._join(), {
28407 				locale: currentLocale,
28408 				sync: sync,
28409 				loadParms: loadParams,
28410 				onLoad: ilib.bind(this, function (data) {
28411 					if (data && !data.invalid) {
28412 						norm = data;
28413 					}
28414 					
28415 					formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
28416 					if (typeof(callback) === 'function') {
28417 						callback(formatted);
28418 					}
28419 				})
28420 			});
28421 		} else {
28422 			formatted = this._doNormalize(options, norm, homeLocale, currentLocale, currentPlan, destinationLocale, destinationPlan, sync, loadParams);
28423 			if (typeof(callback) === 'function') {
28424 				callback(formatted);
28425 			}
28426 		}
28427 	},
28428 	
28429 	/**
28430 	 * This function normalizes the current phone number to a canonical format and returns a
28431 	 * string with that phone number. If parts are missing, this function attempts to fill in 
28432 	 * those parts.<p>
28433 	 * 	  
28434 	 * The options object contains a set of properties that can possibly help normalize
28435 	 * this number by providing "extra" information to the algorithm. The options
28436 	 * parameter may be null or an empty object if no hints can be determined before
28437 	 * this call is made. If any particular hint is not
28438 	 * available, it does not need to be present in the options object.<p>
28439 	 * 
28440 	 * The following is a list of hints that the algorithm will look for in the options
28441 	 * object:
28442 	 * 
28443 	 * <ul>
28444 	 * <li><i>mcc</i> the mobile carrier code of the current network upon which this 
28445 	 * phone is operating. This is translated into an IDD country code. This is 
28446 	 * useful if the number being normalized comes from CNAP (callerid) and the
28447 	 * MCC is known.
28448 	 * <li><i>defaultAreaCode</i> the area code of the phone number of the current
28449 	 * device, if available. Local numbers in a person's contact list are most 
28450 	 * probably in this same area code.
28451 	 * <li><i>country</i> the 2 letter ISO 3166 code of the country if it is
28452 	 * known from some other means such as parsing the physical address of the
28453 	 * person associated with the phone number, or the from the domain name 
28454 	 * of the person's email address
28455 	 * <li><i>networkType</i> specifies whether the phone is currently connected to a
28456 	 * CDMA network or a UMTS network. Valid values are the strings "cdma" and "umts".
28457 	 * If one of those two strings are not specified, or if this property is left off
28458 	 * completely, this method will assume UMTS.
28459 	 * </ul>
28460 	 * 
28461 	 * The following are a list of options that control the behaviour of the normalization:
28462 	 * 
28463 	 * <ul>
28464 	 * <li><i>assistedDialing</i> if this is set to true, the number will be normalized
28465 	 * so that it can dialled directly on the type of network this phone is 
28466 	 * currently connected to. This allows customers to dial numbers or use numbers 
28467 	 * in their contact list that are specific to their "home" region when they are 
28468 	 * roaming and those numbers would not otherwise work with the current roaming 
28469 	 * carrier as they are. The home region is 
28470 	 * specified as the phoneRegion system preference that is settable in the 
28471 	 * regional settings app. With assisted dialling, this method will add or 
28472 	 * remove international direct dialling prefixes and country codes, as well as
28473 	 * national trunk access codes, as required by the current roaming carrier and the
28474 	 * home region in order to dial the number properly. If it is not possible to 
28475 	 * construct a full international dialling sequence from the options and hints given,
28476 	 * this function will not modify the phone number, and will return "undefined".
28477 	 * If assisted dialling is false or not specified, then this method will attempt
28478 	 * to add all the information it can to the number so that it is as fully
28479 	 * specified as possible. This allows two numbers to be compared more easily when
28480 	 * those two numbers were otherwise only partially specified.
28481 	 * <li><i>sms</i> set this option to true for the following conditions: 
28482 	 *   <ul>
28483 	 *   <li>assisted dialing is turned on
28484 	 *   <li>the phone number represents the destination of an SMS message
28485 	 *   <li>the phone is UMTS 
28486 	 *   <li>the phone is SIM-locked to its carrier
28487 	 *   </ul> 
28488 	 * This enables special international direct dialling codes to route the SMS message to
28489 	 * the correct carrier. If assisted dialling is not turned on, this option has no
28490 	 * affect.
28491 	 * <li><i>manualDialing</i> set this option to true if the user is entering this number on
28492 	 * the keypad directly, and false when the number comes from a stored location like a 
28493 	 * contact entry or a call log entry. When true, this option causes the normalizer to 
28494 	 * not perform any normalization on numbers that look like local numbers in the home 
28495 	 * country. If false, all numbers go through normalization. This option only has an effect
28496 	 * when the assistedDialing option is true as well, otherwise it is ignored.
28497 	 * </ul> 
28498 	 * 
28499 	 * If both a set of options and a locale are given, and they offer conflicting
28500 	 * information, the options will take precedence. The idea is that the locale
28501 	 * tells you the region setting that the user has chosen (probably in 
28502 	 * firstuse), whereas the the hints are more current information such as
28503 	 * where the phone is currently operating (the MCC).<p> 
28504 	 * 
28505 	 * This function performs the following types of normalizations with assisted
28506 	 * dialling turned on:
28507 	 * 
28508 	 * <ol>
28509 	 * <li>If the current location of the phone matches the home country, this is a
28510 	 * domestic call.
28511 	 *   <ul> 
28512 	 *   <li>Remove any iddPrefix and countryCode fields, as they are not needed
28513 	 *   <li>Add in a trunkAccess field that may be necessary to call a domestic numbers 
28514 	 *     in the home country
28515 	 *   </ul>
28516 	 * <li> If the current location of the phone does not match the home country,
28517 	 * attempt to form a whole international number.
28518 	 *   <ul>
28519 	 *   <li>Add in the area code if it is missing from the phone number and the area code
28520 	 *     of the current phone is available in the hints
28521 	 *   <li>Add the country dialling code for the home country if it is missing from the 
28522 	 *     phone number
28523 	 *   <li>Add or replace the iddPrefix with the correct one for the current country. The
28524 	 *     phone number will have been parsed with the settings for the home country, so
28525 	 *     the iddPrefix may be incorrect for the
28526 	 *     current country. The iddPrefix for the current country can be "+" if the phone 
28527 	 *     is connected to a UMTS network, and either a "+" or a country-dependent 
28528 	 *     sequences of digits for CDMA networks.
28529 	 *   </ul>
28530 	 * </ol>
28531 	 * 
28532 	 * This function performs the following types of normalization with assisted
28533 	 * dialling turned off:
28534 	 * 
28535 	 * <ul>
28536 	 * <li>Normalize the international direct dialing prefix to be a plus or the
28537 	 * international direct dialling access code for the current country, depending
28538 	 * on the network type.
28539 	 * <li>If a number is a local number (ie. it is missing its area code), 
28540 	 * use a default area code from the hints if available. CDMA phones always know their area 
28541 	 * code, and GSM/UMTS phones know their area code in many instances, but not always 
28542 	 * (ie. not on Vodaphone or Telcel phones). If the default area code is not available, 
28543 	 * do not add it.
28544 	 * <li>In assisted dialling mode, if a number is missing its country code, 
28545 	 * use the current MCC number if
28546 	 * it is available to figure out the current country code, and prepend that 
28547 	 * to the number. If it is not available, leave it off. Also, use that 
28548 	 * country's settings to parse the number instead of the current format 
28549 	 * locale.
28550 	 * <li>For North American numbers with an area code but no trunk access 
28551 	 * code, add in the trunk access code.
28552 	 * <li>For other countries, if the country code is added in step 3, remove the 
28553 	 * trunk access code when required by that country's conventions for 
28554 	 * international calls. If the country requires a trunk access code for 
28555 	 * international calls and it doesn't exist, add one.
28556 	 * </ul>
28557 	 *  
28558 	 * This method modifies the current object, and also returns a string 
28559 	 * containing the normalized phone number that can be compared directly against
28560 	 * other normalized numbers. The canonical format for phone numbers that is 
28561 	 * returned from thhomeLocaleis method is simply an uninterrupted and unformatted string 
28562 	 * of dialable digits.
28563 	 * 
28564 	 * @param {{
28565 	 *   mcc:string,
28566 	 *   defaultAreaCode:string,
28567 	 *   country:string,
28568 	 *   networkType:string,
28569 	 *   assistedDialing:boolean,
28570 	 *   sms:boolean,
28571 	 *   manualDialing:boolean
28572 	 * }} options an object containing options to help in normalizing. 
28573 	 * @return {string|undefined} the normalized string, or undefined if the number
28574 	 * could not be normalized
28575 	 */
28576 	normalize: function(options) {
28577 		var norm,
28578 			sync = true,
28579 			loadParams = {};
28580 			
28581 
28582 		if (options) {
28583 			if (typeof(options.sync) !== 'undefined') {
28584 				sync = !!options.sync;
28585 			}
28586 			
28587 			if (options.loadParams) {
28588 				loadParams = options.loadParams;
28589 			}
28590 		}
28591 		
28592 		// Clone this number, so we don't mess with the original.
28593 		// No need to do this asynchronously because it's a copy constructor which doesn't 
28594 		// load any extra files.
28595 		norm = new PhoneNumber(this);
28596 
28597 		var normalized;
28598 		
28599 		if (options && (typeof(options.mcc) !== 'undefined' || typeof(options.country) !== 'undefined')) {
28600 			new PhoneLocale({
28601 				mcc: options.mcc,
28602 				countryCode: options.countryCode,
28603 				locale: this.locale,
28604 				sync: sync,
28605 				loadParams: loadParams,
28606 				onLoad: ilib.bind(this, function(loc) {
28607 					new NumberingPlan({
28608 						locale: loc,
28609 						sync: sync,
28610 						loadParms: loadParams,
28611 						onLoad: ilib.bind(this, function (plan) {
28612 							this._doReparse(options, norm, this.locale, loc, plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) {
28613 								normalized = fmt;
28614 								
28615 								if (options && typeof(options.onLoad) === 'function') {
28616 									options.onLoad(fmt);
28617 								}
28618 							});
28619 						})
28620 					});
28621 				})
28622 			});
28623 		} else {
28624 			this._doReparse(options, norm, this.locale, this.locale, this.plan, this.destinationLocale, this.destinationPlan, sync, loadParams, function (fmt) {
28625 				normalized = fmt;
28626 				
28627 				if (options && typeof(options.onLoad) === 'function') {
28628 					options.onLoad(fmt);
28629 				}
28630 			});
28631 		}
28632 
28633 		// return the value for the synchronous case
28634 		return normalized;
28635 	}
28636 };
28637 
28638 
28639 /*< PhoneFmt.js */
28640 /*
28641  * phonefmt.js - Represent a phone number formatter.
28642  * 
28643  * Copyright © 2014-2015, JEDLSoft
28644  *
28645  * Licensed under the Apache License, Version 2.0 (the "License");
28646  * you may not use this file except in compliance with the License.
28647  * You may obtain a copy of the License at
28648  *
28649  *     http://www.apache.org/licenses/LICENSE-2.0
28650  *
28651  * Unless required by applicable law or agreed to in writing, software
28652  * distributed under the License is distributed on an "AS IS" BASIS,
28653  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
28654  *
28655  * See the License for the specific language governing permissions and
28656  * limitations under the License.
28657  */
28658 
28659 /*
28660 !depends 
28661 ilib.js 
28662 Locale.js 
28663 NumberingPlan.js
28664 PhoneNumber.js
28665 PhoneLocale.js
28666 Utils.js
28667 JSUtils.js
28668 */
28669 
28670 // !data phonefmt
28671 
28672 
28673 /**
28674  * @class
28675  * Create a new phone number formatter object that formats numbers according to the parameters.<p>
28676  * 
28677  * The options object can contain zero or more of the following parameters:
28678  *
28679  * <ul>
28680  * <li><i>locale</i> locale to use to format this number, or undefined to use the default locale
28681  * <li><i>style</i> the name of style to use to format numbers, or undefined to use the default style
28682  * <li><i>mcc</i> the MCC of the country to use if the number is a local number and the country code is not known
28683  *
28684  * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been 
28685  * parsed. When the onLoad option is given, the address formatter object 
28686  * will attempt to load any missing locale data using the ilib loader callback.
28687  * When the constructor is done (even if the data is already preassembled), the 
28688  * onLoad function is called with the current instance as a parameter, so this
28689  * callback can be used with preassembled or dynamic loading or a mix of the two. 
28690  * 
28691  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
28692  * asynchronously. If this option is given as "false", then the "onLoad"
28693  * callback must be given, as the instance returned from this constructor will
28694  * not be usable for a while. 
28695  *
28696  * <li><i>loadParams</i> - an object containing parameters to pass to the 
28697  * loader callback function when locale data is missing. The parameters are not
28698  * interpretted or modified in any way. They are simply passed along. The object 
28699  * may contain any property/value pairs as long as the calling code is in
28700  * agreement with the loader callback function as to what those parameters mean.
28701  * </ul>
28702  *
28703  * Some regions have more than one style of formatting, and the style parameter
28704  * selects which style the user prefers. An array of style names that this locale
28705  * supports can be found by calling {@link PhoneFmt.getAvailableStyles}. 
28706  * Example phone numbers can be retrieved for each style by calling 
28707  * {@link PhoneFmt.getStyleExample}.
28708  * <p>
28709  *
28710  * If the MCC is given, numbers will be formatted in the manner of the country
28711  * specified by the MCC. If it is not given, but the locale is, the manner of
28712  * the country in the locale will be used. If neither the locale or MCC are not given,
28713  * then the country of the current ilib locale is used. 
28714  *
28715  * @constructor
28716  * @param {Object} options properties that control how this formatter behaves
28717  */
28718 var PhoneFmt = function(options) {
28719 	this.sync = true;
28720 	this.styleName = 'default',
28721 	this.loadParams = {};
28722 
28723 	var locale = new Locale();
28724 
28725 	if (options) {
28726 		if (options.locale) {
28727 			locale = options.locale;
28728 		}
28729 
28730 		if (typeof(options.sync) !== 'undefined') {
28731 			this.sync = !!options.sync;
28732 		}
28733 
28734 		if (options.loadParams) {
28735 			this.loadParams = options.loadParams;
28736 		}
28737 
28738 		if (options.style) {
28739 			this.style = options.style;
28740 		}
28741 	}
28742 
28743 	new PhoneLocale({
28744 		locale: locale,
28745 		mcc: options && options.mcc,
28746 		countryCode: options && options.countryCode,
28747 		onLoad: ilib.bind(this, function (data) {
28748 			/** @type {PhoneLocale} */
28749 			this.locale = data;
28750 
28751 			new NumberingPlan({
28752 				locale: this.locale,
28753 				sync: this.sync,
28754 				loadParms: this.loadParams,
28755 				onLoad: ilib.bind(this, function (plan) {
28756 					/** @type {NumberingPlan} */
28757 					this.plan = plan;
28758 
28759 					Utils.loadData({
28760 						name: "phonefmt.json",
28761 						object: "PhoneFmt",
28762 						locale: this.locale, 
28763 						sync: this.sync,
28764 						loadParams: JSUtils.merge(this.loadParams, {
28765 							returnOne: true
28766 						}),
28767 						callback: ilib.bind(this, function (fmtdata) {
28768 							this.fmtdata = fmtdata;
28769 							
28770 							if (options && typeof(options.onLoad) === 'function') {
28771 								options.onLoad(this);
28772 							}
28773 						})
28774 					});
28775 				})
28776 			});
28777 		})
28778 	});
28779 };
28780 
28781 PhoneFmt.prototype = {
28782 	/**
28783 	 * 
28784 	 * @protected
28785 	 * @param {string} part
28786 	 * @param {Object} formats
28787 	 * @param {boolean} mustUseAll
28788 	 */
28789 	_substituteDigits: function(part, formats, mustUseAll) {
28790 		var formatString,
28791 			formatted = "",
28792 			partIndex = 0,
28793 			templates,
28794 			i;
28795 
28796 		// console.info("Globalization.Phone._substituteDigits: typeof(formats) is " + typeof(formats));
28797 		if (!part) {
28798 			return formatted;
28799 		}
28800 
28801 		if (typeof(formats) === "object") {
28802 			templates = (typeof(formats.template) !== 'undefined') ? formats.template : formats;
28803 			if (part.length > templates.length) {
28804 				// too big, so just use last resort rule.
28805 				throw "part " + part + " is too big. We do not have a format template to format it.";
28806 			}
28807 			// use the format in this array that corresponds to the digit length of this
28808 			// part of the phone number
28809 			formatString =  templates[part.length-1];
28810 			// console.info("Globalization.Phone._substituteDigits: formats is an Array: " + JSON.stringify(formats));
28811 		} else {
28812 			formatString = formats;
28813 		}
28814 
28815 		for (i = 0; i < formatString.length; i++) {
28816 			if (formatString.charAt(i) === "X") {
28817 				formatted += part.charAt(partIndex);
28818 				partIndex++;
28819 			} else {
28820 				formatted += formatString.charAt(i);
28821 			}
28822 		}
28823 		
28824 		if (mustUseAll && partIndex < part.length-1) {
28825 			// didn't use the whole thing in this format? Hmm... go to last resort rule
28826 			throw "too many digits in " + part + " for format " + formatString;
28827 		}
28828 		
28829 		return formatted;
28830 	},
28831 	
28832 	/**
28833 	 * Returns the style with the given name, or the default style if there
28834 	 * is no style with that name.
28835 	 * @protected
28836 	 * @return {{example:string,whole:Object.<string,string>,partial:Object.<string,string>}|Object.<string,string>}
28837 	 */
28838 	_getStyle: function (name, fmtdata) {
28839 		return fmtdata[name] || fmtdata["default"];
28840 	},
28841 
28842 	/**
28843 	 * Do the actual work of formatting the phone number starting at the given
28844 	 * field in the regular field order.
28845 	 * 
28846 	 * @param {!PhoneNumber} number
28847 	 * @param {{
28848 	 *   partial:boolean,
28849 	 *   style:string,
28850 	 *   mcc:string,
28851 	 *   locale:(string|Locale),
28852 	 *   sync:boolean,
28853 	 *   loadParams:Object,
28854 	 *   onLoad:function(string)
28855 	 * }} options Parameters which control how to format the number
28856 	 * @param {number} startField
28857 	 */
28858 	_doFormat: function(number, options, startField, locale, fmtdata, callback) {
28859 		var sync = true,
28860 			loadParams = {},
28861 			temp, 
28862 			templates, 
28863 			fieldName, 
28864 			countryCode, 
28865 			isWhole, 
28866 			style,
28867 			formatted = "",
28868 			styleTemplates,
28869 			lastFieldName;
28870 	
28871 		if (options) {
28872 			if (typeof(options.sync) !== 'undefined') {
28873 				sync = !!options.sync;
28874 			}
28875 		
28876 			if (options.loadParams) {
28877 				loadParams = options.loadParams;
28878 			}
28879 		}
28880 	
28881 		style = this.style; // default style for this formatter
28882 
28883 		// figure out what style to use for this type of number
28884 		if (number.countryCode) {
28885 			// dialing from outside the country
28886 			// check to see if it to a mobile number because they are often formatted differently
28887 			style = (number.mobilePrefix) ? "internationalmobile" : "international";
28888 		} else if (number.mobilePrefix !== undefined) {
28889 			style = "mobile";
28890 		} else if (number.serviceCode !== undefined && typeof(fmtdata["service"]) !== 'undefined') {
28891 			// if there is a special format for service numbers, then use it
28892 			style = "service";
28893 		}
28894 
28895 		isWhole = (!options || !options.partial);
28896 		styleTemplates = this._getStyle(style, fmtdata);
28897 		
28898 		// console.log("Style ends up being " + style + " and using subtype " + (isWhole ? "whole" : "partial"));
28899 		styleTemplates = (isWhole ? styleTemplates.whole : styleTemplates.partial) || styleTemplates;
28900 
28901 		for (var i = startField; i < PhoneNumber._fieldOrder.length; i++) {
28902 			fieldName = PhoneNumber._fieldOrder[i];
28903 			// console.info("format: formatting field " + fieldName + " value: " + number[fieldName]);
28904 			if (number[fieldName] !== undefined) {
28905 				if (styleTemplates[fieldName] !== undefined) {
28906 					templates = styleTemplates[fieldName];
28907 					if (fieldName === "trunkAccess") {
28908 						if (number.areaCode === undefined && number.serviceCode === undefined && number.mobilePrefix === undefined) {
28909 							templates = "X";
28910 						}
28911 					}
28912 					if (lastFieldName && typeof(styleTemplates[lastFieldName].suffix) !== 'undefined') {
28913 						if (fieldName !== "extension" && number[fieldName].search(/[xwtp,;]/i) <= -1) {
28914 							formatted += styleTemplates[lastFieldName].suffix;	
28915 						}
28916 					}
28917 					lastFieldName = fieldName;
28918 					
28919 					// console.info("format: formatting field " + fieldName + " with templates " + JSON.stringify(templates));
28920 					temp = this._substituteDigits(number[fieldName], templates, (fieldName === "subscriberNumber"));
28921 					// console.info("format: formatted is: " + temp);
28922 					formatted += temp;
28923 	
28924 					if (fieldName === "countryCode") {
28925 						// switch to the new country to format the rest of the number
28926 						countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, '');	// fix for NOV-108200
28927 
28928 						new PhoneLocale({
28929 							locale: this.locale,
28930 							sync: sync,							
28931 							loadParms: loadParams,
28932 							countryCode: countryCode,
28933 							onLoad: ilib.bind(this, function (locale) {
28934 								Utils.loadData({
28935 									name: "phonefmt.json",
28936 									object: "PhoneFmt",
28937 									locale: locale,
28938 									sync: sync,
28939 									loadParams: JSUtils.merge(loadParams, {
28940 										returnOne: true
28941 									}),
28942 									callback: ilib.bind(this, function (fmtdata) {
28943 										// console.info("format: switching to region " + locale.region + " and style " + style + " to format the rest of the number ");
28944 										
28945 										var subfmt = "";
28946 
28947 										this._doFormat(number, options, i+1, locale, fmtdata, function (subformat) {
28948 											subfmt = subformat;
28949 											if (typeof(callback) === 'function') {
28950 												callback(formatted + subformat);
28951 											}
28952 										});
28953 										
28954 										formatted += subfmt;
28955 									})
28956 								});
28957 							})
28958 						});
28959 						return formatted;
28960 					}
28961 				} else {
28962 					//console.warn("PhoneFmt.format: cannot find format template for field " + fieldName + ", region " + locale.region + ", style " + style);
28963 					// use default of "minimal formatting" so we don't miss parts because of bugs in the format templates
28964 					formatted += number[fieldName];
28965 				}
28966 			}
28967 		}
28968 		
28969 		if (typeof(callback) === 'function') {
28970 			callback(formatted);
28971 		}
28972 
28973 		return formatted;
28974 	},
28975 	
28976 	/**
28977 	 * Format the parts of a phone number appropriately according to the settings in 
28978 	 * this formatter instance.
28979 	 *  
28980 	 * The options can contain zero or more of these properties:
28981 	 * 
28982 	 * <ul>
28983 	 * <li><i>partial</i> boolean which tells whether or not this phone number 
28984 	 * represents a partial number or not. The default is false, which means the number 
28985 	 * represents a whole number. 
28986 	 * <li><i>style</i> style to use to format the number, if different from the 
28987 	 * default style or the style specified in the constructor
28988 	 * <li><i>locale</i> The locale with which to parse the number. This gives a clue as to which
28989      * numbering plan to use.
28990      * <li><i>mcc</i> The mobile carrier code (MCC) associated with the carrier that the phone is 
28991      * currently connected to, if known. This also can give a clue as to which numbering plan to
28992      * use
28993      * <li><i>onLoad</i> - a callback function to call when the date format object is fully 
28994      * loaded. When the onLoad option is given, the DateFmt object will attempt to
28995      * load any missing locale data using the ilib loader callback.
28996      * When the constructor is done (even if the data is already preassembled), the 
28997      * onLoad function is called with the current instance as a parameter, so this
28998      * callback can be used with preassembled or dynamic loading or a mix of the two.
28999      * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
29000      * asynchronously. If this option is given as "false", then the "onLoad"
29001      * callback must be given, as the instance returned from this constructor will
29002      * not be usable for a while.
29003      * <li><i>loadParams</i> - an object containing parameters to pass to the 
29004      * loader callback function when locale data is missing. The parameters are not
29005      * interpretted or modified in any way. They are simply passed along. The object 
29006      * may contain any property/value pairs as long as the calling code is in
29007      * agreement with the loader callback function as to what those parameters mean.
29008 	 * </ul>
29009 	 *      
29010 	 * The partial parameter specifies whether or not the phone number contains
29011 	 * a partial phone number or if it is a whole phone number. A partial 
29012 	 * number is usually a number as the user is entering it with a dial pad. The
29013 	 * reason is that certain types of phone numbers should be formatted differently
29014 	 * depending on whether or not it represents a whole number. Specifically, SMS
29015 	 * short codes are formatted differently.<p>
29016 	 * 
29017 	 * Example: a subscriber number of "48773" in the US would get formatted as:
29018 	 * 
29019 	 * <ul>
29020 	 * <li>partial: 487-73  (perhaps the user is in the process of typing a whole phone 
29021 	 * number such as 487-7379)
29022 	 * <li>whole:   48773   (this is the entire SMS short code)
29023 	 * </ul>
29024 	 * 
29025 	 * Any place in the UI where the user types in phone numbers, such as the keypad in 
29026 	 * the phone app, should pass in partial: true to this formatting routine. All other 
29027 	 * places, such as the call log in the phone app, should pass in partial: false, or 
29028 	 * leave the partial flag out of the parameters entirely. 
29029 	 * 
29030 	 * @param {!PhoneNumber} number object containing the phone number to format
29031 	 * @param {{
29032 	 *   partial:boolean,
29033 	 *   style:string,
29034 	 *   mcc:string,
29035 	 *   locale:(string|Locale),
29036 	 *   sync:boolean,
29037 	 *   loadParams:Object,
29038 	 *   onLoad:function(string)
29039 	 * }} options Parameters which control how to format the number
29040 	 * @return {string} Returns the formatted phone number as a string.
29041 	 */
29042 	format: function (number, options) {
29043 		var formatted = "",
29044 		    callback;
29045 
29046 		callback = options && options.onLoad;
29047 
29048 		try {
29049 			this._doFormat(number, options, 0, this.locale, this.fmtdata, function (fmt) {
29050 				formatted = fmt;
29051 				
29052 				if (typeof(callback) === 'function') {
29053 					callback(fmt);
29054 				}
29055 			});
29056 		} catch (e) {
29057 			if (typeof(e) === 'string') { 
29058 				// console.warn("caught exception: " + e + ". Using last resort rule.");
29059 				// if there was some exception, use this last resort rule
29060 				formatted = "";
29061 				for (var field in PhoneNumber._fieldOrder) {
29062 					if (typeof field === 'string' && typeof PhoneNumber._fieldOrder[field] === 'string' && number[PhoneNumber._fieldOrder[field]] !== undefined) {
29063 						// just concatenate without any formatting
29064 						formatted += number[PhoneNumber._fieldOrder[field]];
29065 						if (PhoneNumber._fieldOrder[field] === 'countryCode') {
29066 							formatted += ' ';		// fix for NOV-107894
29067 						}
29068 					}
29069 				}
29070 			} else {
29071 				throw e;
29072 			}
29073 			
29074 			if (typeof(callback) === 'function') {
29075 				callback(formatted);
29076 			}
29077 		}
29078 		return formatted;
29079 	},
29080 	
29081 	/**
29082 	 * Return an array of names of all available styles that can be used with the current 
29083 	 * formatter.
29084 	 * @return {Array.<string>} an array of names of styles that are supported by this formatter
29085 	 */
29086 	getAvailableStyles: function () {
29087 		var ret = [],
29088 			style;
29089 
29090 		if (this.fmtdata) {
29091 			for (style in this.fmtdata) {
29092 				if (this.fmtdata[style].example) {
29093 					ret.push(style);
29094 				}
29095 			}
29096 		}
29097 		return ret;
29098 	},
29099 	
29100 	/**
29101 	 * Return an example phone number formatted with the given style.
29102 	 * 
29103 	 * @param {string|undefined} style style to get an example of, or undefined to use
29104 	 * the current default style for this formatter
29105 	 * @return {string|undefined} an example phone number formatted according to the 
29106 	 * given style, or undefined if the style is not recognized or does not have an 
29107 	 * example 
29108 	 */
29109 	getStyleExample: function (style) {
29110 		return this.fmtdata[style].example || undefined;
29111 	}
29112 };
29113 
29114 
29115 /*< PhoneGeoLocator.js */
29116 /*
29117  * PhoneGeoLocator.js - Represent a phone number geolocator object.
29118  * 
29119  * Copyright © 2014-2015, 2018 JEDLSoft
29120  *
29121  * Licensed under the Apache License, Version 2.0 (the "License");
29122  * you may not use this file except in compliance with the License.
29123  * You may obtain a copy of the License at
29124  *
29125  *     http://www.apache.org/licenses/LICENSE-2.0
29126  *
29127  * Unless required by applicable law or agreed to in writing, software
29128  * distributed under the License is distributed on an "AS IS" BASIS,
29129  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29130  *
29131  * See the License for the specific language governing permissions and
29132  * limitations under the License.
29133  */
29134 
29135 /*
29136 !depends 
29137 ilib.js 
29138 NumberingPlan.js
29139 PhoneLocale.js
29140 PhoneNumber.js
29141 Utils.js
29142 JSUtils.js
29143 ResBundle.js
29144 */
29145 
29146 // !data iddarea area extarea extstates phoneres
29147 
29148 
29149 
29150 /**
29151  * @class
29152  * Create an instance that can geographically locate a phone number.<p>
29153  * 
29154  * The location of the number is calculated according to the following rules:
29155  * 
29156  * <ol>
29157  * <li>If the areaCode property is undefined or empty, or if the number specifies a 
29158  * country code for which we do not have information, then the area property may be 
29159  * missing from the returned object. In this case, only the country object will be returned.
29160  * 
29161  * <li>If there is no area code, but there is a mobile prefix, service code, or emergency 
29162  * code, then a fixed string indicating the type of number will be returned.
29163  * 
29164  * <li>The country object is filled out according to the countryCode property of the phone
29165  * number. 
29166  * 
29167  * <li>If the phone number does not have an explicit country code, the MCC will be used if
29168  * it is available. The country code can be gleaned directly from the MCC. If the MCC 
29169  * of the carrier to which the phone is currently connected is available, it should be 
29170  * passed in so that local phone numbers will look correct.
29171  * 
29172  * <li>If the country's dialling plan mandates a fixed length for phone numbers, and a 
29173  * particular number exceeds that length, then the area code will not be given on the
29174  * assumption that the number has problems in the first place and we cannot guess
29175  * correctly.
29176  * </ol>
29177  * 
29178  * The returned area property varies in specificity according
29179  * to the locale. In North America, the area is no finer than large parts of states
29180  * or provinces. In Germany and the UK, the area can be as fine as small towns.<p>
29181  * 
29182  * If the number passed in is invalid, no geolocation will be performed. If the location
29183  * information about the country where the phone number is located is not available,
29184  * then the area information will be missing and only the country will be available.<p>
29185  * 
29186  * The options parameter can contain any one of the following properties:
29187  * 
29188  * <ul>
29189  * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and
29190  * areas if available. For example, if the locale property is given as "en-US" (English for USA), 
29191  * but the phone number being geolocated is in Germany, then this class would return the the names
29192  * of the country (Germany) and region inside of Germany in English instead of German. That is, a 
29193  * phone number in Munich and return the country "Germany" and the area code "Munich"
29194  * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 
29195  * If translations are not available, the region and area names are given in English, which should 
29196  * always be available.
29197  * <li><i>mcc</i> The mcc of the current mobile carrier, if known.
29198  * 
29199  * <li><i>onLoad</i> - a callback function to call when the data for the
29200  * locale is fully loaded. When the onLoad option is given, this object 
29201  * will attempt to load any missing locale data using the ilib loader callback.
29202  * When the constructor is done (even if the data is already preassembled), the 
29203  * onLoad function is called with the current instance as a parameter, so this
29204  * callback can be used with preassembled or dynamic loading or a mix of the two. 
29205  * 
29206  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
29207  * asynchronously. If this option is given as "false", then the "onLoad"
29208  * callback must be given, as the instance returned from this constructor will
29209  * not be usable for a while. 
29210  *
29211  * <li><i>loadParams</i> - an object containing parameters to pass to the 
29212  * loader callback function when locale data is missing. The parameters are not
29213  * interpretted or modified in any way. They are simply passed along. The object 
29214  * may contain any property/value pairs as long as the calling code is in
29215  * agreement with the loader callback function as to what those parameters mean.
29216  * </ul>
29217  * 
29218  * @constructor
29219  * @param {Object} options parameters controlling the geolocation of the phone number.
29220  */
29221 var PhoneGeoLocator = function(options) {
29222 	var sync = true,
29223 		loadParams = {},
29224 		locale = ilib.getLocale();
29225 
29226 	if (options) {
29227 		if (options.locale) {
29228 			locale = options.locale;
29229 		}
29230 
29231 		if (typeof(options.sync) === 'boolean') {
29232 			sync = options.sync;
29233 		}
29234 		
29235 		if (options.loadParams) {
29236 			loadParams = options.loadParams;
29237 		}
29238 	}
29239 	
29240 	new PhoneLocale({
29241 		locale: locale,
29242 		mcc: options && options.mcc,
29243 		countryCode: options && options.countryCode,
29244 		sync: sync,
29245 		loadParams: loadParams,
29246 		onLoad: ilib.bind(this, function (loc) {
29247 			this.locale = loc;
29248 			new NumberingPlan({
29249 				locale: this.locale,
29250 				sync: sync,
29251 				loadParams: loadParams,
29252 				onLoad: ilib.bind(this, function (plan) {
29253 					this.plan = plan;
29254 					
29255 					new ResBundle({
29256 						locale: this.locale,
29257 						name: "phoneres",
29258 						sync: sync,
29259 						loadParams: loadParams,
29260 						onLoad: ilib.bind(this, function (rb) {
29261 							this.rb = rb;
29262 							
29263 							Utils.loadData({
29264 								name: "iddarea.json",
29265 								object: "PhoneGeoLocator",
29266 								nonlocale: true,
29267 								sync: sync,
29268 								loadParams: loadParams,
29269 								callback: ilib.bind(this, function (data) {
29270 									this.regiondata = data;
29271 									Utils.loadData({
29272 										name: "area.json",
29273 										object: "PhoneGeoLocator",
29274 										locale: this.locale,
29275 										sync: sync,
29276 										loadParams: JSUtils.merge(loadParams, {
29277 											returnOne: true
29278 										}),
29279 										callback: ilib.bind(this, function (areadata) {
29280 											this.areadata = areadata;
29281 		
29282 											if (options && typeof(options.onLoad) === 'function') {
29283 												options.onLoad(this);
29284 											}
29285 										})
29286 									});
29287 								})
29288 							});
29289 						})
29290 					});
29291 				})
29292 			});
29293 		})
29294 	});
29295 };
29296 
29297 PhoneGeoLocator.prototype = {
29298 	/**
29299 	 * @private
29300 	 * 
29301 	 * Used for locales where the area code is very general, and you need to add in
29302 	 * the initial digits of the subscriber number in order to get the area
29303 	 * 
29304 	 * @param {string} number
29305 	 * @param {Object} stateTable
29306 	 */
29307 	_parseAreaAndSubscriber: function (number, stateTable) {
29308 		var ch,
29309 			i,
29310 			handlerMethod,
29311 			newState,
29312 			consumed,
29313 			lastLeaf,
29314 			currentState,
29315 			dot = 14;	// special transition which matches all characters. See AreaCodeTableMaker.java
29316 
29317 		if (!number || !stateTable) {
29318 			// can't parse anything
29319 			return undefined;
29320 		}
29321 
29322 		//console.log("GeoLocator._parseAreaAndSubscriber: parsing number " + number);
29323 
29324 		currentState = stateTable;
29325 		i = 0;
29326 		while (i < number.length) {
29327 			ch = PhoneNumber._getCharacterCode(number.charAt(i));
29328 			if (ch >= 0) {
29329 				// newState = stateData.states[state][ch];
29330 				newState = currentState.s && currentState.s[ch];
29331 				
29332 				if (!newState && currentState.s && currentState.s[dot]) {
29333 					newState = currentState.s[dot];
29334 				}
29335 				
29336 				if (typeof(newState) === 'object') {
29337 					if (typeof(newState.l) !== 'undefined') {
29338 						// save for latter if needed
29339 						lastLeaf = newState;
29340 						consumed = i;
29341 					}
29342 					// console.info("recognized digit " + ch + " continuing...");
29343 					// recognized digit, so continue parsing
29344 					currentState = newState;
29345 					i++;
29346 				} else {
29347 					if (typeof(newState) === 'undefined' || newState === 0) {
29348 						// this is possibly a look-ahead and it didn't work... 
29349 						// so fall back to the last leaf and use that as the
29350 						// final state
29351 						newState = lastLeaf;
29352 						i = consumed;
29353 					}
29354 					
29355 					if ((typeof(newState) === 'number' && newState) ||
29356 						(typeof(newState) === 'object' && typeof(newState.l) !== 'undefined')) {
29357 						// final state
29358 						var stateNumber = typeof(newState) === 'number' ? newState : newState.l;
29359 						handlerMethod = PhoneNumber._states[stateNumber];
29360 
29361 						//console.info("reached final state " + newState + " handler method is " + handlerMethod + " and i is " + i);
29362 	
29363 						return (handlerMethod === "area") ? number.substring(0, i+1) : undefined;
29364 					} else {
29365 						// failed parse. Either no last leaf to fall back to, or there was an explicit
29366 						// zero in the table
29367 						break;
29368 					}
29369 				}
29370 			} else if (ch === -1) {
29371 				// non-transition character, continue parsing in the same state
29372 				i++;
29373 			} else {
29374 				// should not happen
29375 				// console.info("skipping character " + ch);
29376 				// not a digit, plus, pound, or star, so this is probably a formatting char. Skip it.
29377 				i++;
29378 			}
29379 		}
29380 		return undefined;
29381 	},
29382 	/**
29383 	 * @private
29384 	 * @param prefix
29385 	 * @param table
29386 	 * @returns
29387 	 */
29388 	_matchPrefix: function(prefix, table)  {
29389 		var i, matchedDot, matchesWithDots = [];
29390 
29391 		if (table[prefix]) {
29392 			return table[prefix];
29393 		}
29394 		for (var entry in table) {
29395 			if (entry && typeof(entry) === 'string') {
29396 				i = 0;
29397 				matchedDot = false;
29398 				while (i < entry.length && (entry.charAt(i) === prefix.charAt(i) || entry.charAt(i) === '.')) {
29399 					if (entry.charAt(i) === '.') {
29400 						matchedDot = true;
29401 					}
29402 					i++;
29403 				}
29404 				if (i >= entry.length) {
29405 					if (matchedDot) {
29406 						matchesWithDots.push(entry);
29407 					} else {
29408 						return table[entry];
29409 					}
29410 				}
29411 			}
29412 		}
29413 
29414 		// match entries with dots last, so sort the matches so that the entry with the 
29415 		// most dots sorts last. The entry that ends up at the beginning of the list is
29416 		// the best match because it has the fewest dots
29417 		if (matchesWithDots.length > 0) {
29418 			matchesWithDots.sort(function (left, right) {
29419 				return (right < left) ? -1 : ((left < right) ? 1 : 0);
29420 			});
29421 			return table[matchesWithDots[0]];
29422 		}
29423 		
29424 		return undefined;
29425 	},
29426 	/**
29427 	 * @private
29428 	 * @param number
29429 	 * @param data
29430 	 * @param locale
29431 	 * @param plan
29432 	 * @param options
29433 	 * @returns {Object}
29434 	 */
29435 	_getAreaInfo: function(number, data, locale, plan, options) {
29436 		var sync = true,
29437 			ret = {}, 
29438 			countryCode, 
29439 			areaInfo, 
29440 			temp, 
29441 			areaCode, 
29442 			geoTable, 
29443 			tempNumber, 
29444 			prefix;
29445 
29446 		if (options && typeof(options.sync) === 'boolean') {
29447 			sync = options.sync;
29448 		}
29449 
29450 		prefix = number.areaCode || number.serviceCode;
29451 		geoTable = data;
29452 		
29453 		if (prefix !== undefined) {
29454 			if (plan.getExtendedAreaCode()) {
29455 				// for countries where the area code is very general and large, and you need a few initial
29456 				// digits of the subscriber number in order find the actual area
29457 				tempNumber = prefix + number.subscriberNumber;
29458 				tempNumber = tempNumber.replace(/[wWpPtT\+#\*]/g, '');	// fix for NOV-108200
29459 		
29460 				Utils.loadData({
29461 					name: "extarea.json",
29462 					object: "PhoneGeoLocator", 
29463 					locale: locale,
29464 					sync: sync,
29465 					loadParams: JSUtils.merge((options && options.loadParams) || {}, {returnOne: true}),
29466 					callback: ilib.bind(this, function (data) {
29467 						this.extarea = data;
29468 						Utils.loadData({
29469 							name: "extstates.json",
29470 							object: "PhoneGeoLocator", 
29471 							locale: locale,
29472 							sync: sync,
29473 							loadParams: JSUtils.merge((options && options.loadParams) || {}, {returnOne: true}),
29474 							callback: ilib.bind(this, function (data) {
29475 								this.extstates = data;
29476 								geoTable = this.extarea;
29477 								if (this.extarea && this.extstates) {
29478 									prefix = this._parseAreaAndSubscriber(tempNumber, this.extstates);
29479 								}
29480 								
29481 								if (!prefix) {
29482 									// not a recognized prefix, so now try the general table
29483 									geoTable = this.areadata;
29484 									prefix = number.areaCode || number.serviceCode;					
29485 								}
29486 
29487 								if ((!plan.fieldLengths || 
29488 								  plan.getFieldLength('maxLocalLength') === undefined ||
29489 								  !number.subscriberNumber ||
29490 								 	number.subscriberNumber.length <= plan.fieldLengths('maxLocalLength'))) {
29491 								  	areaInfo = this._matchPrefix(prefix, geoTable);
29492 									if (areaInfo && areaInfo.sn && areaInfo.ln) {
29493 										//console.log("Found areaInfo " + JSON.stringify(areaInfo));
29494 										ret.area = {
29495 											sn: this.rb.getString(areaInfo.sn).toString(),
29496 											ln: this.rb.getString(areaInfo.ln).toString()
29497 										};
29498 									}
29499 								}		
29500 							})
29501 						});
29502 					})
29503 				});
29504 
29505 			} else if (!plan || 
29506 					plan.getFieldLength('maxLocalLength') === undefined || 
29507 					!number.subscriberNumber ||
29508 					number.subscriberNumber.length <= plan.getFieldLength('maxLocalLength')) {
29509 				if (geoTable) {
29510 					areaCode = prefix.replace(/[wWpPtT\+#\*]/g, '');
29511 					areaInfo = this._matchPrefix(areaCode, geoTable);
29512 
29513 					if (areaInfo && areaInfo.sn && areaInfo.ln) {
29514 						ret.area = {
29515 							sn: this.rb.getString(areaInfo.sn).toString(),
29516 							ln: this.rb.getString(areaInfo.ln).toString()
29517 						};
29518 					} else if (number.serviceCode) {
29519 						ret.area = {
29520 							sn: this.rb.getString("Service Number").toString(),
29521 							ln: this.rb.getString("Service Number").toString()
29522 						};
29523 					}
29524 				} else {
29525 					countryCode = number.locale._mapRegiontoCC(this.locale.getRegion());
29526 					if (countryCode !== "0" && this.regiondata) {
29527 						temp = this.regiondata[countryCode];
29528 						if (temp && temp.sn) {
29529 							ret.country = {
29530 								sn: this.rb.getString(temp.sn).toString(),
29531 								ln: this.rb.getString(temp.ln).toString(),
29532 								code: this.locale.getRegion()
29533 							};
29534 						}
29535 					}
29536 				}
29537 			} else {
29538 				countryCode = number.locale._mapRegiontoCC(this.locale.getRegion());
29539 				if (countryCode !== "0" && this.regiondata) {
29540 					temp = this.regiondata[countryCode];
29541 					if (temp && temp.sn) {
29542 						ret.country = {
29543 							sn: this.rb.getString(temp.sn).toString(),
29544 							ln: this.rb.getString(temp.ln).toString(),
29545 							code: this.locale.getRegion()
29546 						};
29547 					}
29548 				}
29549 			}
29550 
29551 		} else if (number.mobilePrefix) {
29552 			ret.area = {
29553 				sn: this.rb.getString("Mobile Number").toString(),
29554 				ln: this.rb.getString("Mobile Number").toString()
29555 			};
29556 		} else if (number.emergency) {
29557 			ret.area = {
29558 				sn: this.rb.getString("Emergency Services Number").toString(),
29559 				ln: this.rb.getString("Emergency Services Number").toString()
29560 			};
29561 		}
29562 
29563 		return ret;
29564 	},
29565 	/**
29566 	 * Returns a the location of the given phone number, if known. 
29567 	 * The returned object has 2 properties, each of which has an sn (short name) 
29568 	 * and an ln (long name) string. Additionally, the country code, if given,
29569 	 * includes the 2 letter ISO code for the recognized country.
29570 	 *	 	{
29571 	 *			"country": {
29572 	 *	        	"sn": "North America",
29573 	 *            	"ln": "North America and the Caribbean Islands",
29574 	 *				"code": "us"
29575 	 *         	 },
29576 	 *         	 "area": {
29577 	 *       	    "sn": "California",
29578 	 *          	 "ln": "Central California: San Jose, Los Gatos, Milpitas, Sunnyvale, Cupertino, Gilroy"
29579 	 *         	 }
29580 	 *    	 }
29581 	 * 
29582 	 * The location name is subject to the following rules:
29583 	 *
29584 	 * If the areaCode property is undefined or empty, or if the number specifies a 
29585 	 * country code for which we do not have information, then the area property may be 
29586 	 * missing from the returned object. In this case, only the country object will be returned.
29587 	 *
29588 	 * If there is no area code, but there is a mobile prefix, service code, or emergency 
29589 	 * code, then a fixed string indicating the type of number will be returned.
29590 	 * 
29591 	 * The country object is filled out according to the countryCode property of the phone
29592 	 * number. 
29593 	 * 
29594 	 * If the phone number does not have an explicit country code, the MCC will be used if
29595 	 * it is available. The country code can be gleaned directly from the MCC. If the MCC 
29596 	 * of the carrier to which the phone is currently connected is available, it should be 
29597 	 * passed in so that local phone numbers will look correct.
29598 	 * 
29599 	 * If the country's dialling plan mandates a fixed length for phone numbers, and a 
29600 	 * particular number exceeds that length, then the area code will not be given on the
29601 	 * assumption that the number has problems in the first place and we cannot guess
29602 	 * correctly.
29603 	 *
29604 	 * The returned area property varies in specificity according
29605 	 * to the locale. In North America, the area is no finer than large parts of states
29606 	 * or provinces. In Germany and the UK, the area can be as fine as small towns.
29607 	 *
29608 	 * The strings returned from this function are already localized to the 
29609 	 * given locale, and thus are ready for display to the user.
29610 	 *
29611 	 * If the number passed in is invalid, an empty object is returned. If the location
29612 	 * information about the country where the phone number is located is not available,
29613 	 * then the area information will be missing and only the country will be returned.
29614      *
29615 	 * The options parameter can contain any one of the following properties:
29616  	 * 
29617  	 * <ul>
29618  	 * <li><i>locale</i> The locale parameter is used to load translations of the names of regions and
29619  	 * areas if available. For example, if the locale property is given as "en-US" (English for USA), 
29620  	 * but the phone number being geolocated is in Germany, then this class would return the the names
29621  	 * of the country (Germany) and region inside of Germany in English instead of German. That is, a 
29622  	 * phone number in Munich and return the country "Germany" and the area code "Munich"
29623  	 * instead of "Deutschland" and "München". The default display locale is the current ilib locale. 
29624  	 * If translations are not available, the region and area names are given in English, which should 
29625  	 * always be available.
29626  	 * <li><i>mcc</i> The mcc of the current mobile carrier, if known.
29627  	 * 
29628  	 * <li><i>onLoad</i> - a callback function to call when the data for the
29629  	 * locale is fully loaded. When the onLoad option is given, this object 
29630  	 * will attempt to load any missing locale data using the ilib loader callback.
29631  	 * When the constructor is done (even if the data is already preassembled), the 
29632  	 * onLoad function is called with the current instance as a parameter, so this
29633  	 * callback can be used with preassembled or dynamic loading or a mix of the two. 
29634  	 * 
29635  	 * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
29636  	 * asynchronously. If this option is given as "false", then the "onLoad"
29637  	 * callback must be given, as the instance returned from this constructor will
29638  	 * not be usable for a while. 
29639  	 *
29640  	 * <li><i>loadParams</i> - an object containing parameters to pass to the 
29641  	 * loader callback function when locale data is missing. The parameters are not
29642  	 * interpretted or modified in any way. They are simply passed along. The object 
29643  	 * may contain any property/value pairs as long as the calling code is in
29644  	 * agreement with the loader callback function as to what those parameters mean.
29645  	 * </ul>
29646 	 * 
29647 	 * @param {PhoneNumber} number phone number to locate
29648 	 * @param {Object} options options governing the way this ares is loaded
29649 	 * @return {Object} an object  
29650 	 * that describes the country and the area in that country corresponding to this
29651 	 * phone number. Each of the country and area contain a short name (sn) and long
29652 	 * name (ln) that describes the location.
29653 	 */
29654 	locate: function(number, options) {
29655 		var loadParams = {},
29656 			ret = {}, 
29657 			region, 
29658 			countryCode, 
29659 			temp, 
29660 			plan,
29661 			areaResult,
29662 			phoneLoc = this.locale,
29663 			sync = true;
29664 
29665 		if (number === undefined || typeof(number) !== 'object' || !(number instanceof PhoneNumber)) {
29666 			return ret;
29667 		}
29668 
29669 		if (options) {
29670 			if (typeof(options.sync) !== 'undefined') {
29671 				sync = !!options.sync;
29672 			}
29673 		
29674 			if (options.loadParams) {
29675 				loadParams = options.loadParams;
29676 			}
29677 		}
29678 
29679 		// console.log("GeoLocator.locate: looking for geo for number " + JSON.stringify(number));
29680 		region = this.locale.getRegion();
29681 		if (number.countryCode !== undefined && this.regiondata) {
29682 			countryCode = number.countryCode.replace(/[wWpPtT\+#\*]/g, '');
29683 			temp = this.regiondata[countryCode];
29684 			phoneLoc = number.destinationLocale;
29685 			plan = number.destinationPlan;
29686 			ret.country = {
29687 				sn: this.rb.getString(temp.sn).toString(),
29688 				ln: this.rb.getString(temp.ln).toString(),
29689 				code: phoneLoc.getRegion()
29690 			};
29691 		}
29692 		
29693 		if (!plan) {
29694 			plan = this.plan;
29695 		}
29696 		
29697 		Utils.loadData({
29698 			name: "area.json",
29699 			object: "PhoneGeoLocator",
29700 			locale: phoneLoc,
29701 			sync: sync,
29702 			loadParams: JSUtils.merge(loadParams, {
29703 				returnOne: true
29704 			}),
29705 			callback: ilib.bind(this, function (areadata) {
29706 				if (areadata) {
29707 					this.areadata = areadata;	
29708 				}
29709 				areaResult = this._getAreaInfo(number, this.areadata, phoneLoc, plan, options);
29710 				ret = JSUtils.merge(ret, areaResult);
29711 
29712 				if (ret.country === undefined) {
29713 					countryCode = number.locale._mapRegiontoCC(region);
29714 					
29715 					if (countryCode !== "0" && this.regiondata) {
29716 						temp = this.regiondata[countryCode];
29717 						if (temp && temp.sn) {
29718 							ret.country = {
29719 								sn: this.rb.getString(temp.sn).toString(),
29720 								ln: this.rb.getString(temp.ln).toString(),
29721 								code: this.locale.getRegion()
29722 							};
29723 						}
29724 					}
29725 				}
29726 			})
29727 		});
29728 		
29729 		return ret;
29730 	},
29731 	
29732 	/**
29733 	 * Returns a string that describes the ISO-3166-2 country code of the given phone
29734 	 * number.<p> 
29735 	 * 
29736 	 * If the phone number is a local phone number and does not contain
29737 	 * any country information, this routine will return the region for the current
29738 	 * formatter instance.
29739      *
29740 	 * @param {PhoneNumber} number An PhoneNumber instance
29741 	 * @return {string}
29742 	 */
29743 	country: function(number) {
29744 		var countryCode,
29745 			region,
29746 			phoneLoc;
29747 
29748 		if (!number || !(number instanceof PhoneNumber)) {
29749 			return "";
29750 		}
29751 
29752 		phoneLoc = number.locale;
29753 
29754 		region = (number.countryCode && phoneLoc._mapCCtoRegion(number.countryCode)) ||
29755 			(number.locale && number.locale.region) || 
29756 			phoneLoc.locale.getRegion() ||
29757 			this.locale.getRegion();
29758 
29759 		countryCode = number.countryCode || phoneLoc._mapRegiontoCC(region);
29760 		
29761 		if (number.areaCode) {
29762 			region = phoneLoc._mapAreatoRegion(countryCode, number.areaCode);
29763 		} else if (countryCode === "33" && number.serviceCode) {
29764 			// french departments are in the service code, not the area code
29765 			region = phoneLoc._mapAreatoRegion(countryCode, number.serviceCode);
29766 		}		
29767 		return region;
29768 	}
29769 };
29770 
29771 
29772 /*< Measurement.js */
29773 /*
29774  * Measurement.js - Measurement unit superclass
29775  *
29776  * Copyright © 2014-2015, 2018 JEDLSoft
29777  *
29778  * Licensed under the Apache License, Version 2.0 (the "License");
29779  * you may not use this file except in compliance with the License.
29780  * You may obtain a copy of the License at
29781  *
29782  *     http://www.apache.org/licenses/LICENSE-2.0
29783  *
29784  * Unless required by applicable law or agreed to in writing, software
29785  * distributed under the License is distributed on an "AS IS" BASIS,
29786  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
29787  *
29788  * See the License for the specific language governing permissions and
29789  * limitations under the License.
29790  */
29791 
29792 // !depends JSUtils.js MathUtils.js Locale.js
29793 
29794 
29795 function round(number, precision) {
29796     var factor = Math.pow(10, precision);
29797     return MathUtils.halfdown(number * factor) / factor;
29798 }
29799 
29800 /**
29801  * @class
29802  * Superclass for measurement instances that contains shared functionality
29803  * and defines the interface. <p>
29804  *
29805  * This class is never instantiated on its own. Instead, measurements should
29806  * be created using the {@link MeasurementFactory} function, which creates the
29807  * correct subclass based on the given parameters.<p>
29808  *
29809  * @param {Object=} options options controlling the construction of this instance
29810  * @private
29811  * @constructor
29812  */
29813 var Measurement = function(options) {
29814     if (options) {
29815         if (typeof(options.unit) !== 'undefined') {
29816             this.originalUnit = options.unit;
29817             this.unit = this.normalizeUnits(options.unit) || options.unit;
29818         }
29819 
29820         if (typeof(options.amount) === 'object') {
29821             if (options.amount.getMeasure() === this.getMeasure()) {
29822                 this.amount = options.amount.convert(this.unit);
29823             } else {
29824                 throw "Cannot convert unit " + options.amount.unit + " to a " + this.getMeasure();
29825             }
29826         } else if (typeof(options.amount) !== 'undefined') {
29827             this.amount = Number(options.amount);
29828         }
29829 
29830         if (typeof(this.ratios[this.unit]) === 'undefined') {
29831             throw "Unknown unit: " + options.unit;
29832         }
29833     }
29834 };
29835 
29836 /**
29837  * @private
29838  */
29839 Measurement._constructors = {};
29840 
29841 Measurement.prototype = {
29842     /**
29843      * Return the normalized name of the given units. If the units are
29844      * not recognized, this method returns its parameter unmodified.<p>
29845      *
29846      * Examples:
29847      *
29848      * <ui>
29849      * <li>"metres" gets normalized to "meter"<br>
29850      * <li>"ml" gets normalized to "milliliter"<br>
29851      * <li>"foobar" gets normalized to "foobar" (no change because it is not recognized)
29852      * </ul>
29853      *
29854      * @param {string} name name of the units to normalize.
29855      * @returns {string} normalized name of the units
29856      */
29857     normalizeUnits: function(name) {
29858         return (this.constructor && (Measurement.getUnitId(this.constructor, name) ||
29859             Measurement.getUnitIdCaseInsensitive(this.constructor, name))) ||
29860             name;
29861     },
29862 
29863     /**
29864      * Return the normalized units used in this measurement.
29865      * @return {string} name of the unit of measurement
29866      */
29867     getUnit: function() {
29868         return this.unit;
29869     },
29870 
29871     /**
29872      * Return the units originally used to construct this measurement
29873      * before it was normalized.
29874      * @return {string} name of the unit of measurement
29875      */
29876     getOriginalUnit: function() {
29877         return this.originalUnit;
29878     },
29879 
29880     /**
29881      * Return the numeric amount of this measurement.
29882      * @return {number} the numeric amount of this measurement
29883      */
29884     getAmount: function() {
29885         return this.amount;
29886     },
29887 
29888     /**
29889      * Return the type of this measurement. Examples are "mass",
29890      * "length", "speed", etc. Measurements can only be converted
29891      * to measurements of the same type.<p>
29892      *
29893      * The type of the units is determined automatically from the
29894      * units. For example, the unit "grams" is type "mass". Use the
29895      * static call {@link Measurement.getAvailableUnits}
29896      * to find out what units this version of ilib supports.
29897      *
29898      * @return {string} the name of the type of this measurement
29899      */
29900     getMeasure: function() {},
29901 
29902     /**
29903      * Return an array of all units that this measurement types supports.
29904      *
29905      * @return {Array.<string>} an array of all units that this measurement
29906      * types supports
29907      */
29908     getMeasures: function () {
29909         return Object.keys(this.ratios);
29910     },
29911 
29912     /**
29913      * Return the name of the measurement system that the current
29914      * unit is a part of.
29915      *
29916      * @returns {string} the name of the measurement system for
29917      * the units of this measurement
29918      */
29919     getMeasurementSystem: function() {
29920         if (this.unit) {
29921             if (JSUtils.indexOf(this.systems.uscustomary, this.unit) > -1) {
29922                 return "uscustomary";
29923             }
29924     
29925             if (JSUtils.indexOf(this.systems.imperial, this.unit) > -1) {
29926                 return "imperial";
29927             }
29928         }
29929         return "metric";
29930     },
29931 
29932     /**
29933      * Localize the measurement to the commonly used measurement in that locale. For example
29934      * If a user's locale is "en-US" and the measurement is given as "60 kmh",
29935      * the formatted number should be automatically converted to the most appropriate
29936      * measure in the other system, in this case, mph. The formatted result should
29937      * appear as "37.3 mph".
29938      *
29939      * @param {string} locale current locale string
29940      * @returns {Measurement} a new instance that is converted to locale
29941      */
29942     localize: function(locale) {
29943         var to;
29944         var toSystem = Measurement.getMeasurementSystemForLocale(locale);
29945         var fromSystem = this.getMeasurementSystem();
29946         if (toSystem === fromSystem) return this; // already there
29947         to = this.systems.conversions[fromSystem] &&
29948             this.systems.conversions[fromSystem][toSystem] &&
29949             this.systems.conversions[fromSystem][toSystem][this.unit];
29950 
29951         return to ? this.newUnit({
29952             unit: to,
29953             amount: this.convert(to)
29954         }) : this;
29955     },
29956 
29957     /**
29958      * Return the amount of the current measurement when converted to the
29959      * given measurement unit. Measurements can only be converted
29960      * to other measurements of the same type.<p>
29961      *
29962      * @param {string} to the name of the units to convert this measurement to
29963      * @return {number|undefined} the amount corresponding to the requested unit
29964      */
29965     convert: function(to) {
29966         if (!to || typeof(this.ratios[this.normalizeUnits(to)]) === 'undefined') {
29967             return undefined;
29968         }
29969 
29970         var from = this.getUnitIdCaseInsensitive(this.unit) || this.unit;
29971         to = this.getUnitIdCaseInsensitive(to) || to;
29972         if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
29973             return undefined;
29974         }
29975 
29976         var fromRow = this.ratios[from];
29977         var toRow = this.ratios[to];
29978         return this.amount * fromRow[toRow[0]];
29979     },
29980 
29981     /**
29982      * Return a new measurement instance that is converted to a different
29983      * measurement system. Measurements can only be converted
29984      * to other measurements of the same type.<p>
29985      *
29986      * @param {string} measurementSystem the name of the system to convert to
29987      * @return {Measurement} a new measurement in the given system, or the
29988      * current measurement if it is already in the given system or could not
29989      * be converted
29990      */
29991     convertSystem: function(measurementSystem) {
29992         if (!measurementSystem || measurementSystem === this.getMeasurementSystem()) {
29993             return this;
29994         }
29995         var map = this.systems.conversions[this.getMeasurementSystem()][measurementSystem];
29996         var newunit = map && map[this.unit];
29997         if (!newunit) return this;
29998 
29999         return this.newUnit({
30000             unit: newunit,
30001             amount: this.convert(newunit)
30002         });
30003     },
30004 
30005     /**
30006      * Scale the measurement unit to an acceptable level. The scaling
30007      * happens so that the integer part of the amount is as small as
30008      * possible without being below zero. This will result in the
30009      * largest units that can represent this measurement without
30010      * fractions. Measurements can only be scaled to other measurements
30011      * of the same type.
30012      *
30013      * @param {string=} measurementsystem the name of the system to scale to
30014      * @param {Object=} units mapping from the measurement system to the units to use
30015      * for this scaling. If this is not defined, this measurement type will use the
30016      * set of units that it knows about for the given measurement system
30017      * @return {Measurement} a new instance that is scaled to the
30018      * right level
30019      */
30020     scale: function(measurementsystem, units) {
30021         var systemName = this.getMeasurementSystem();
30022         var mSystem;
30023         if (units) {
30024             mSystem = (units[measurementsystem] && JSUtils.indexOf(units[measurementsystem], this.unit) > -1) ?
30025                 units[measurementsystem] : units[systemName];
30026         }
30027         if (!mSystem) {
30028             mSystem = (this.systems[measurementsystem] && JSUtils.indexOf(this.systems[measurementsystem], this.unit) > -1) ?
30029                 this.systems[measurementsystem] : this.systems[systemName];
30030         }
30031         if (!mSystem) {
30032             // cannot find the system to scale within... just return the measurement as is
30033             return this;
30034         }
30035 
30036         return this.newUnit(this.scaleUnits(mSystem));
30037     },
30038 
30039     /**
30040      * Expand the current measurement such that any fractions of the current unit
30041      * are represented in terms of smaller units in the same system instead of fractions
30042      * of the current unit. For example, "6.25 feet" may be represented as
30043      * "6 feet 4 inches" instead. The return value is an array of measurements which
30044      * are progressively smaller until the smallest unit in the system is reached
30045      * or until there is a whole number of any unit along the way.
30046      *
30047      * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30048      * or undefined if the system can be inferred from the current measure
30049      * @param {Array.<string>=} units object containing a mapping between the measurement system
30050      * and an array of units to use to restrict the expansion to
30051      * @param {function(number):number=} constrain a function that constrains
30052      * a number according to the display options
30053      * @param {boolean=} scale if true, rescale all of the units so that the
30054      * largest unit is the largest one with a non-fractional number. If false, then
30055      * the current unit stays the largest unit.
30056      * @return {Array.<Measurement>} an array of new measurements in order from
30057      * the current units to the smallest units in the system which together are the
30058      * same measurement as this one
30059      */
30060     expand: function(measurementsystem, units, constrain, scale) {
30061         var systemName = this.getMeasurementSystem();
30062         var mSystem = (units && units[systemName]) ? units[systemName] : (this.systems[systemName] || this.systems.metric);
30063 
30064         return this.list(mSystem, this.ratios, constrain, scale).map(function(item) {
30065             return this.newUnit(item);
30066         }.bind(this));
30067     },
30068 
30069     /**
30070      * Convert the current measurement to a list of measures
30071      * and amounts. This method will autoScale the current measurement
30072      * to the largest measure in the given measures list such that the
30073      * amount of that measure is still greater than or equal to 1. From
30074      * there, it will truncate that measure to a whole
30075      * number and then it will calculate the remainder in terms of
30076      * each of the smaller measures in the given list.<p>
30077      *
30078      * For example, if a person's height is given as 70.5 inches, and
30079      * the list of measures is ["mile", "foot", "inch"], then it will
30080      * scale the amount to 5 feet, 10.5 inches. The amount is not big
30081      * enough to have any whole miles, so that measure is not used.
30082      * The first measure will be "foot" because it is the first one
30083      * in the measure list where the there is an amount of them that
30084      * is greater than or equal to 1. The return value in this example
30085      * would be:
30086      *
30087      * <pre>
30088      * [
30089      *   {
30090      *     "unit": "foot",
30091      *     "amount": 5
30092      *   },
30093      *   {
30094      *     "unit": "inch",
30095      *     "amount": 10.5
30096      *   }
30097      * ]
30098      * </pre>
30099      *
30100      * Note that all measures except the smallest will be returned
30101      * as whole numbers. The smallest measure will contain any possible
30102      * fractional remainder.
30103      *
30104      * @param {Array.<string>|undefined} measures array of measure names to
30105      * convert this measure to
30106      * @param {Object} ratios the conversion ratios
30107      * table for the measurement type
30108      * @param {function (number): number=} constrain a function that constrains
30109      * a number according to the display options
30110      * @param {boolean=} scale if true, rescale all of the units so that the
30111      * largest unit is the largest one with a non-fractional number. If false, then
30112      * the current unit stays the largest unit.
30113      * @returns {Array.<{unit: String, amount: Number}>} the conversion
30114      * of the current measurement into an array of unit names and
30115      * their amounts
30116      */
30117     list: function(measures, ratios, constrain, scale) {
30118         var row = ratios[this.unit];
30119         var ret = [];
30120         var scaled;
30121         var unit = this.unit;
30122         var amount = this.amount;
30123         constrain = constrain || round;
30124 
30125         var start = JSUtils.indexOf(measures, this.unit);
30126 
30127         if (scale || start === -1) {
30128             start = measures.length-1;
30129         }
30130 
30131         if (this.unit !== measures[0]) {
30132             // if this unit is not the smallest measure in the system, we have to convert
30133             unit = measures[0];
30134             amount = this.amount * row[ratios[unit][0]];
30135             row = ratios[unit];
30136         }
30137 
30138         // convert to smallest measure
30139         amount = constrain(amount);
30140         // go backwards so we get from the largest to the smallest units in order
30141         for (var j = start; j > 0; j--) {
30142             unit = measures[j];
30143             scaled = amount * row[ratios[unit][0]];
30144             var xf = Math.floor(scaled);
30145             if (xf) {
30146                 var item = {
30147                     unit: unit,
30148                     amount: xf
30149                 };
30150                 ret.push(item);
30151 
30152                 amount -= xf * ratios[unit][ratios[measures[0]][0]];
30153             }
30154         }
30155 
30156         // last measure is rounded/constrained, not truncated
30157         if (amount !== 0) {
30158             ret.push({
30159                 unit: measures[0],
30160                 amount: constrain(amount)
30161             });
30162         }
30163 
30164         return ret;
30165     },
30166 
30167     /**
30168      * @private
30169      */
30170     scaleUnits: function(mSystem) {
30171         var tmp, munit, amount = 18446744073709551999;
30172         var fromRow = this.ratios[this.unit];
30173 
30174         for (var m = 0; m < mSystem.length; m++) {
30175             tmp = this.amount * fromRow[this.ratios[mSystem[m]][0]];
30176             if ((tmp >= 1 && tmp < amount) || amount === 18446744073709551999) {
30177                 amount = tmp;
30178                 munit = mSystem[m];
30179             }
30180         }
30181 
30182         return {
30183             unit: munit,
30184             amount: amount
30185         };
30186     },
30187 
30188     /**
30189      * @private
30190      *
30191      * Return the normalized units identifier for the given unit. This looks up the units
30192      * in the aliases list and returns the normalized unit id.
30193      *
30194      * @param {string} unit the unit to find
30195      * @returns {string|undefined} the normalized identifier for the given unit, or
30196      * undefined if there is no such unit in this type of measurement
30197      */
30198     getUnitId: function(unit) {
30199         if (!unit) return undefined;
30200 
30201         if (this.aliases && typeof(this.aliases[unit]) !== 'undefined') {
30202             return this.aliases[unit];
30203         }
30204 
30205         if (this.ratios && typeof(this.ratios[unit]) !== 'undefined') {
30206             return unit;
30207         }
30208 
30209         return undefined;
30210     },
30211 
30212     /**
30213      * Return the normalized units identifier for the given unit, searching case-insensitively.
30214      * This has the risk that things may match erroneously because many short form unit strings
30215      * are case-sensitive. This should method be used as a last resort if no case-sensitive match
30216      * is found amongst all the different types of measurements.
30217      *
30218      * @param {string} unit the unit to find
30219      * @returns {string|undefined} the normalized identifier for the given unit, or
30220      * undefined if there is no such unit in this type of measurement
30221      */
30222     getUnitIdCaseInsensitive: function(unit) {
30223         if (!unit) return undefined;
30224 
30225         // try with the original case first, just in case that works
30226         var ret = this.getUnitId(unit);
30227         if (ret) return ret;
30228 
30229         var u = unit.toLowerCase();
30230         if (this.aliasesLower && typeof(this.aliasesLower[u]) !== 'undefined') {
30231             return this.aliasesLower[u];
30232         }
30233 
30234         return undefined;
30235     }
30236 };
30237 
30238 /**
30239  * Return the normalized units identifier for the given unit. This looks up the units
30240  * in the aliases list and returns the normalized unit id.
30241  *
30242  * @static
30243  * @param {function(...)} measurement name of the the class of measure being searched
30244  * @param {string} unit the unit to find
30245  * @returns {string|undefined} the normalized identifier for the given unit, or
30246  * undefined if there is no such unit in this type of measurement
30247  */
30248 Measurement.getUnitId = function(measurement, unit) {
30249     if (!unit) return undefined;
30250 
30251     if (typeof(measurement.aliases[unit]) !== 'undefined') {
30252         return measurement.aliases[unit];
30253     }
30254 
30255     if (measurement.ratios && typeof(measurement.ratios[unit]) !== 'undefined') {
30256         return unit;
30257     }
30258 
30259     return undefined;
30260 };
30261 
30262 /**
30263  * Return the normalized units identifier for the given unit, searching case-insensitively.
30264  * This has the risk that things may match erroneously because many short form unit strings
30265  * are case-sensitive. This should method be used as a last resort if no case-sensitive match
30266  * is found amongst all the different types of measurements.
30267  *
30268  * @static
30269  * @param {function(...)} measurement name of the class of measure being searched
30270  * @param {string} unit the unit to find
30271  * @returns {string|undefined} the normalized identifier for the given unit, or
30272  * undefined if there is no such unit in this type of measurement
30273  */
30274 Measurement.getUnitIdCaseInsensitive = function(measurement, unit) {
30275     if (!unit) return undefined;
30276     var u = unit.toLowerCase();
30277 
30278     // try this first, just in case
30279     var ret = Measurement.getUnitId(measurement, unit);
30280     if (ret) return ret;
30281 
30282     if (measurement.aliases && !measurement.aliasesLower) {
30283         measurement.aliasesLower = {};
30284         for (var a in measurement.aliases) {
30285             measurement.aliasesLower[a.toLowerCase()] = measurement.aliases[a];
30286         }
30287     }
30288 
30289     if (typeof(measurement.aliasesLower[u]) !== 'undefined') {
30290         return measurement.aliasesLower[u];
30291     }
30292 
30293     return undefined;
30294 };
30295 
30296 // Hard-code these because CLDR has incorrect data, plus this is small so we don't
30297 // want to do an async load just to get it.
30298 // Source: https://en.wikipedia.org/wiki/Metrication#Overview
30299 var systems = {
30300     "uscustomary": ["US", "FM", "MH", "LR", "PR", "PW", "GU", "WS", "AS", "VI", "MP"],
30301     "imperial": ["GB", "MM"]
30302 };
30303 
30304 // every other country in the world is metric. Myanmar (MM) is adopting metric by 2019
30305 // supposedly, and Liberia is as well
30306 
30307 /**
30308 * Return the name of the measurement system in use in the given locale.
30309 *
30310 * @param {string|Locale} locale the locale spec or Locale instance of the
30311 *
30312 * @returns {string} the name of the measurement system
30313 */
30314 Measurement.getMeasurementSystemForLocale = function(locale) {
30315   var l = typeof(locale) === "object" ? locale : new Locale(locale);
30316   var region = l.getRegion();
30317 
30318   if (region) {
30319       if (JSUtils.indexOf(systems.uscustomary, region) > -1) {
30320           return "uscustomary";
30321       } else if (JSUtils.indexOf(systems.imperial, region) > -1) {
30322           return "imperial";
30323       }
30324   }
30325 
30326   return "metric";
30327 };
30328 
30329 
30330 
30331 /*< UnknownUnit.js */
30332 /*
30333  * Unknown.js - Dummy unit conversions for unknown types
30334  *
30335  * Copyright © 2014-2015, JEDLSoft
30336  *
30337  * Licensed under the Apache License, Version 2.0 (the "License");
30338  * you may not use this file except in compliance with the License.
30339  * You may obtain a copy of the License at
30340  *
30341  *     http://www.apache.org/licenses/LICENSE-2.0
30342  *
30343  * Unless required by applicable law or agreed to in writing, software
30344  * distributed under the License is distributed on an "AS IS" BASIS,
30345  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30346  *
30347  * See the License for the specific language governing permissions and
30348  * limitations under the License.
30349  */
30350 
30351 // !depends Measurement.js
30352 
30353 
30354 /**
30355  * @class
30356  * Create a new unknown measurement instance.
30357  *
30358  * @constructor
30359  * @extends Measurement
30360  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
30361  * the construction of this instance
30362  */
30363 var UnknownUnit = function (options) {
30364 
30365     this.ratios = {};
30366     this.aliases = UnknownUnit.aliases;
30367     this.aliasesLower = UnknownUnit.aliases;
30368     this.systems = UnknownUnit.systems;
30369 
30370     if (options) {
30371         this.unit = options.unit;
30372         this.amount = options.amount;
30373     }
30374 };
30375 
30376 UnknownUnit.prototype = new Measurement();
30377 UnknownUnit.prototype.parent = Measurement;
30378 UnknownUnit.prototype.constructor = UnknownUnit;
30379 
30380 UnknownUnit.systems = {
30381     "metric": [],
30382     "uscustomary": [],
30383     "imperial": [],
30384     "conversions": {
30385         "metric": {},
30386         "uscustomary": {},
30387         "imperial": {}
30388     }
30389 };
30390 
30391 UnknownUnit.aliases = {
30392 	"unknown":"unknown"
30393 };
30394 
30395 /**
30396  * Return the type of this measurement. Examples are "mass",
30397  * "length", "speed", etc. Measurements can only be converted
30398  * to measurements of the same type.<p>
30399  *
30400  * The type of the units is determined automatically from the
30401  * units. For example, the unit "grams" is type "mass". Use the
30402  * static call {@link Measurement.getAvailableUnits}
30403  * to find out what units this version of ilib supports.
30404  *
30405  * @return {string} the name of the type of this measurement
30406  */
30407 UnknownUnit.prototype.getMeasure = function() {
30408 	return "unknown";
30409 };
30410 
30411 /**
30412  * Return a new measurement instance that is converted to a new
30413  * measurement unit. Measurements can only be converted
30414  * to measurements of the same type.<p>
30415  *
30416  * @param {string} to The name of the units to convert to
30417  * @return {number|undefined} the converted measurement
30418  * or undefined if the requested units are for a different
30419  * measurement type
30420  */
30421 UnknownUnit.prototype.convert = function(to) {
30422 	return undefined;
30423 };
30424 
30425 /**
30426  * Convert a unknown to another measure.
30427  * @static
30428  * @param {string} to unit to convert to
30429  * @param {string} from unit to convert from
30430  * @param {number} unknown amount to be convert
30431  * @returns {number|undefined} the converted amount
30432  */
30433 UnknownUnit.convert = function(to, from, unknown) {
30434     return undefined;
30435 };
30436 
30437 /**
30438  * Localize the measurement to the commonly used measurement in that locale. For example
30439  * If a user's locale is "en-US" and the measurement is given as "60 kmh",
30440  * the formatted number should be automatically converted to the most appropriate
30441  * measure in the other system, in this case, mph. The formatted result should
30442  * appear as "37.3 mph".
30443  *
30444  * @param {string} locale current locale string
30445  * @returns {Measurement} a new instance that is converted to locale
30446  */
30447 UnknownUnit.prototype.localize = function(locale) {
30448     return new UnknownUnit({
30449         unit: this.unit,
30450         amount: this.amount
30451     });
30452 };
30453 
30454 /**
30455  * Scale the measurement unit to an acceptable level. The scaling
30456  * happens so that the integer part of the amount is as small as
30457  * possible without being below zero. This will result in the
30458  * largest units that can represent this measurement without
30459  * fractions. Measurements can only be scaled to other measurements
30460  * of the same type.
30461  *
30462  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30463  * or undefined if the system can be inferred from the current measure
30464  * @return {Measurement} a new instance that is scaled to the
30465  * right level
30466  */
30467 UnknownUnit.prototype.scale = function(measurementsystem) {
30468     return new UnknownUnit({
30469         unit: this.unit,
30470         amount: this.amount
30471     });
30472 };
30473 
30474 /**
30475  * Expand the current measurement such that any fractions of the current unit
30476  * are represented in terms of smaller units in the same system instead of fractions
30477  * of the current unit. For example, "6.25 feet" may be represented as
30478  * "6 feet 4 inches" instead. The return value is an array of measurements which
30479  * are progressively smaller until the smallest unit in the system is reached
30480  * or until there is a whole number of any unit along the way.
30481  *
30482  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30483  * or undefined if the system can be inferred from the current measure
30484  * @return {Array.<Measurement>} an array of new measurements in order from
30485  * the current units to the smallest units in the system which together are the
30486  * same measurement as this one
30487  */
30488 UnknownUnit.prototype.expand = function(measurementsystem) {
30489     return [this]; // nothing to expand
30490 }
30491 
30492 /**
30493  * @private
30494  * @static
30495  */
30496 UnknownUnit.getMeasures = function () {
30497 	return [];
30498 };
30499 
30500 
30501 /*< AreaUnit.js */
30502 /*
30503  * AreaUnit.js - Unit conversions for area
30504  *
30505  * Copyright © 2014-2015, 2018 JEDLSoft
30506  *
30507  * Licensed under the Apache License, Version 2.0 (the "License");
30508  * you may not use this file except in compliance with the License.
30509  * You may obtain a copy of the License at
30510  *
30511  *     http://www.apache.org/licenses/LICENSE-2.0
30512  *
30513  * Unless required by applicable law or agreed to in writing, software
30514  * distributed under the License is distributed on an "AS IS" BASIS,
30515  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30516  *
30517  * See the License for the specific language governing permissions and
30518  * limitations under the License.
30519  */
30520 
30521 /*
30522 !depends
30523 Measurement.js
30524 */
30525 
30526 
30527 /**
30528  * @class
30529  * Create a new area measurement instance.
30530  * @constructor
30531  * @extends Measurement
30532  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
30533  * the construction of this instance
30534  */
30535 var AreaUnit = function (options) {
30536     this.unit = "square-meter";
30537     this.amount = 0;
30538 
30539     this.ratios = AreaUnit.ratios;
30540     this.aliases = AreaUnit.aliases;
30541     this.aliasesLower = AreaUnit.aliasesLower;
30542     this.systems = AreaUnit.systems;
30543 
30544     this.parent.call(this, options);
30545 };
30546 
30547 AreaUnit.prototype = new Measurement();
30548 AreaUnit.prototype.parent = Measurement;
30549 AreaUnit.prototype.constructor = AreaUnit;
30550 
30551 AreaUnit.ratios = {
30552     /*               index		square cm,		square meter,   hectare,   	square km, 	, square inch 	square foot, 		square yard, 	  	  	acre,			    square mile			        */
30553     "square-centimeter":[1,   	1,				0.0001,			1e-8,	    1e-10,        0.15500031,	0.00107639104,		0.000119599005,			2.47105381e-8,		3.86102159e-11 		],
30554     "square-meter": 	[2,   	10000,			1,              1e-4,       1e-6,         1550,    	 	10.7639,    	  	1.19599,   				0.000247105,		3.861e-7     	    ],
30555     "hectare":      	[3,	 	100000000,  	10000,          1,          0.01,         1.55e+7, 	  	107639,     	 	11959.9,   				2.47105	,			0.00386102    	    ],
30556     "square-kilometer": [4,	  	10000000000, 	1e+6,          	100,        1,	          1.55e+9, 	  	1.076e+7,   	 	1.196e+6,  				247.105 ,   		0.386102     	    ],
30557     "square-inch":  	[5,	  	6.4516,			0.00064516,     6.4516e-8,  6.4516e-10,   1,			0.0069444444444444, 0.0007716051, 			1.5942e-7,			2.491e-10    	    ],
30558     "square-foot":  	[6,		929.0304,		0.092903,       9.2903e-6,  9.2903e-8,    144,			1,          	  	0.111111,  				2.2957e-5,			3.587e-8    		],
30559     "square-yard":  	[7,		8361.2736,		0.836127,       8.3613e-5,  8.3613e-7,    1296,    	  	9,          	  	1,         				0.000206612,		3.2283e-7    	    ],
30560     "acre":         	[8,		40468564.2,		4046.86,        0.404686,   0.00404686,   6.273e+6,	  	43560,      	  	4840,      				1,		    		0.0015625    	    ],
30561     "square-mile":  	[9,	   	2.58998811e+10,	2.59e+6,        258.999,    2.58999,      4.014e+9,	 	2.788e+7,   	  	3.098e+6,  				640,     			1   	     		]
30562 }
30563 
30564 /**
30565  * Return the type of this measurement. Examples are "mass",
30566  * "length", "speed", etc. Measurements can only be converted
30567  * to measurements of the same type.<p>
30568  *
30569  * The type of the units is determined automatically from the
30570  * units. For example, the unit "grams" is type "mass". Use the
30571  * static call {@link Measurement.getAvailableUnits}
30572  * to find out what units this version of ilib supports.
30573  *
30574  * @return {string} the name of the type of this measurement
30575  */
30576 AreaUnit.prototype.getMeasure = function() {
30577     return "area";
30578 };
30579 
30580 /**
30581  * Return a new instance of this type of measurement.
30582  * 
30583  * @param {Object} params parameters to the constructor
30584  * @return {Measurement} a measurement subclass instance
30585  */
30586 AreaUnit.prototype.newUnit = function(params) {
30587     return new AreaUnit(params);
30588 };
30589 
30590 AreaUnit.aliases = {
30591     "square centimeter":"square-centimeter",
30592     "square centimeters":"square-centimeter",
30593     "square centimetre":"square-centimeter",
30594     "square centimetres":"square-centimeter",
30595     "sq centimeter":"square-centimeter",
30596     "sq centimeters":"square-centimeter",
30597     "sq centimetre":"square-centimeter",
30598     "sq centimetres":"square-centimeter",
30599     "square cm":"square-centimeter",
30600     "sq cm":"square-centimeter",
30601     "cm2":"square-centimeter",
30602     "cm²":"square-centimeter",
30603     "square kilometer":"square-kilometer",
30604     "square kilometre":"square-kilometer",
30605     "square kilometers":"square-kilometer",
30606     "square kilometres":"square-kilometer",
30607     "sq kilometer":"square-kilometer",
30608     "sq kilometre":"square-kilometer",
30609     "sq kilometers":"square-kilometer",
30610     "sq kilometres":"square-kilometer",
30611     "square km":"square-kilometer",
30612     "sq km":"square-kilometer",
30613     "km2":"square-kilometer",
30614     "km²":"square-kilometer",
30615     "hectare":"hectare",
30616     "ha":"hectare",
30617     "square meter": "square-meter",
30618     "square meters":"square-meter",
30619     "square metre": "square-meter",
30620     "square metres": "square-meter",
30621     "sq meter": "square-meter",
30622     "sq meters":"square-meter",
30623     "sq metre": "square-meter",
30624     "sq metres": "square-meter",
30625     "sqm":"square-meter",
30626     "m2": "square-meter",
30627     "m²":"square-meter",
30628     "square mile":"square-mile",
30629     "square miles":"square-mile",
30630     "square mi":"square-mile",
30631     "sq mi":"square-mile",
30632     "mi2":"square-mile",
30633     "mi²":"square-mile",
30634     "acre": "acre",
30635     "acres":"acre",
30636     "square yard": "square-yard",
30637     "square yards":"square-yard",
30638     "sq yard": "square-yard",
30639     "sq yards": "square-yard",
30640     "sq yrd": "square-yard",
30641     "sq yrds": "square-yard",
30642     "yard2":"square-yard",
30643     "yard²":"square-yard",
30644     "yrd2":"square-yard",
30645     "yrd²":"square-yard",
30646     "yd2":"square-yard",
30647     "yd²":"square-yard",
30648     "square foot": "square-foot",
30649     "square feet": "square-foot",
30650     "sq ft":"square-foot",
30651     "ft2":"square-foot",
30652     "ft²":"square-foot",
30653     "square inch":"square-inch",
30654     "square inches":"square-inch",
30655     "in2":"square-inch",
30656     "in²":"square-inch"
30657 };
30658 
30659 (function() {
30660     AreaUnit.aliasesLower = {};
30661     for (var a in AreaUnit.aliases) {
30662         AreaUnit.aliasesLower[a.toLowerCase()] = AreaUnit.aliases[a];
30663     }
30664 })();
30665 
30666 /**
30667  * Convert a Area to another measure.
30668  * @static
30669  * @param to {string} unit to convert to
30670  * @param from {string} unit to convert from
30671  * @param area {number} amount to be convert
30672  * @returns {number|undefined} the converted amount
30673  */
30674 AreaUnit.convert = function(to, from, area) {
30675     from = Measurement.getUnitIdCaseInsensitive(AreaUnit, from) || from;
30676     to = Measurement.getUnitIdCaseInsensitive(AreaUnit, to) || to;
30677 	var fromRow = AreaUnit.ratios[from];
30678 	var toRow = AreaUnit.ratios[to];
30679 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
30680 		return undefined;
30681 	}
30682 	return area* fromRow[toRow[0]];
30683 };
30684 
30685 /**
30686  * @private
30687  * @static
30688  */
30689 AreaUnit.getMeasures = function () {
30690     return Object.keys(AreaUnit.ratios);
30691 };
30692 
30693 AreaUnit.systems = {
30694     "metric": [
30695         "square-centimeter",
30696         "square-meter",
30697         "hectare",
30698         "square-kilometer"
30699     ],
30700     "imperial": [
30701         "square-inch",
30702         "square-foot",
30703         "square-yard",
30704         "acre",
30705         "square-mile"
30706     ],
30707     "uscustomary": [
30708         "square-inch",
30709         "square-foot",
30710         "square-yard",
30711         "acre",
30712         "square-mile"
30713     ],
30714     "conversions": {
30715         "metric": {
30716             "uscustomary": {
30717                 "square-centimeter" : "square-inch",
30718                 "square-meter" : "square-yard",
30719                 "hectare" : "acre",
30720                 "square-kilometer" : "square-mile"
30721             },
30722             "imperial": {
30723                 "square-centimeter" : "square-inch",
30724                 "square-meter" : "square-yard",
30725                 "hectare" : "acre",
30726                 "square-kilometer" : "square-mile"
30727             }
30728         },
30729         "uscustomary": {
30730             "metric": {
30731                 "square-inch" : "square-centimeter",
30732                 "square-foot" : "square-meter",
30733                 "square-yard" : "square-meter",
30734                 "acre" : "hectare",
30735                 "square-mile" : "square-kilometer"
30736             }
30737         },
30738         "imperial": {
30739             "metric": {
30740                 "square-inch" : "square-centimeter",
30741                 "square-foot" : "square-meter",
30742                 "square-yard" : "square-meter",
30743                 "acre" : "hectare",
30744                 "square-mile" : "square-kilometer"
30745             }
30746         }
30747     }
30748 };
30749 
30750 //register with the factory method
30751 Measurement._constructors["area"] = AreaUnit;
30752 
30753 
30754 /*< DigitalStorageUnit.js */
30755 /*
30756  * DigitalStorageUnit.js - Unit conversions for Digital Storage
30757  *
30758  * Copyright © 2014-2015, 2018 JEDLSoft
30759  *
30760  * Licensed under the Apache License, Version 2.0 (the "License");
30761  * you may not use this file except in compliance with the License.
30762  * You may obtain a copy of the License at
30763  *
30764  *     http://www.apache.org/licenses/LICENSE-2.0
30765  *
30766  * Unless required by applicable law or agreed to in writing, software
30767  * distributed under the License is distributed on an "AS IS" BASIS,
30768  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30769  *
30770  * See the License for the specific language governing permissions and
30771  * limitations under the License.
30772  */
30773 
30774 /*
30775 !depends
30776 Measurement.js
30777 JSUtils.js
30778 */
30779 
30780 
30781 /**
30782  * @class
30783  * Create a new DigitalStorage measurement instance.
30784  *
30785  * @constructor
30786  * @extends Measurement
30787  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
30788  * the construction of this instance
30789  */
30790 var DigitalStorageUnit = function (options) {
30791 	this.unit = "byte";
30792 	this.amount = 0;
30793 
30794     this.ratios = DigitalStorageUnit.ratios;
30795     this.aliases = DigitalStorageUnit.aliases;
30796     this.aliasesLower = DigitalStorageUnit.aliasesLower;
30797     this.systems = DigitalStorageUnit.systems;
30798 
30799     this.parent.call(this, options);
30800 };
30801 
30802 DigitalStorageUnit.prototype = new Measurement();
30803 DigitalStorageUnit.prototype.parent = Measurement;
30804 DigitalStorageUnit.prototype.constructor = DigitalStorageUnit;
30805 
30806 DigitalStorageUnit.ratios = {
30807     /*            #    bit             byte            kb              kB              mb              mB              gb               gB               tb               tB               pb               pB   */
30808 	"bit":      [ 1,   1,              0.125,          0.0009765625,   1.220703125e-4, 9.536743164e-7, 1.192092896e-7, 9.313225746e-10, 1.164153218e-10, 9.094947017e-13, 1.136868377e-13, 8.881784197e-16, 1.110223025e-16 ],
30809     "byte":     [ 2,   8,              1,              0.0078125,      0.0009765625,   7.629394531e-6, 9.536743164e-7, 7.450580597e-9,  9.313225746e-10, 7.275957614e-12, 9.094947017e-13, 7.105427358e-15, 8.881784197e-16 ],
30810     "kilobit":  [ 3,   1024,           128,            1,              0.125,          0.0009765625,   1.220703125e-4, 9.536743164e-7,  1.192092896e-7,  9.313225746e-10, 1.164153218e-10, 9.094947017e-13, 1.136868377e-13 ],
30811     "kilobyte": [ 4,   8192,           1024,           8,              1,              0.0078125,      0.0009765625,   7.629394531e-6,  9.536743164e-7,  7.450580597e-9,  9.313225746e-10, 7.275957614e-12, 9.094947017e-13 ],
30812     "megabit":  [ 5,   1048576,        131072,         1024,           128,            1,              0.125,          0.0009765625,    1.220703125e-4,  9.536743164e-7,  1.192092896e-7,  9.313225746e-10, 1.164153218e-10 ],
30813     "megabyte": [ 6,   8388608,        1048576,        8192,           1024,           8,              1,              0.0078125,       0.0009765625,    7.629394531e-6,  9.536743164e-7,  7.450580597e-9,  9.313225746e-10 ],
30814     "gigabit":  [ 7,   1073741824,     134217728,      1048576,        131072,         1024,           128,            1,               0.125,           0.0009765625,    1.220703125e-4,  9.536743164e-7,  1.192092896e-7  ],
30815     "gigabyte": [ 8,   8589934592,     1073741824,     8388608,        1048576,        8192,           1024,           8,               1,               0.0078125,       0.0009765625,    7.629394531e-6,  9.536743164e-7  ],
30816     "terabit":  [ 9,   1.099511628e12, 137438953472,   1073741824,     134217728,      1048576,        131072,         1024,            128,             1,               0.125,           0.0009765625,    1.220703125e-4  ],
30817     "terabyte": [ 10,  8.796093022e12, 1.099511628e12, 8589934592,     1073741824,     8388608,        1048576,        8192,            1024,            8,               1,               0.0078125,       0.0009765625    ],
30818     "petabit":  [ 11,  1.125899907e15, 1.407374884e14, 1.099511628e12, 137438953472,   1073741824,     134217728,      1048576,         131072,          1024,            128,             1,               0.125           ],
30819     "petabyte": [ 12,  9.007199255e15, 1.125899907e15, 8.796093022e12, 1.099511628e12, 8589934592,     1073741824,     8388608,         1048576,         8192,            1024,            8,               1               ]
30820 };
30821 
30822 /**
30823  * Return a new instance of this type of measurement.
30824  * 
30825  * @param {Object} params parameters to the constructor
30826  * @return {Measurement} a measurement subclass instance
30827  */
30828 DigitalStorageUnit.prototype.newUnit = function(params) {
30829     return new DigitalStorageUnit(params);
30830 };
30831 
30832 DigitalStorageUnit.systems = {
30833     "metric": [],
30834     "uscustomary": [],
30835     "imperial": [],
30836     "conversions": {
30837         "metric": {},
30838         "uscustomary": {},
30839         "imperial": {}
30840     }
30841 };
30842 
30843 DigitalStorageUnit.bitSystem = [
30844     "bit",
30845     "kilobit",
30846     "megabit",
30847     "gigabit",
30848     "terabit",
30849     "petabit"
30850 ];
30851 DigitalStorageUnit.byteSystem = [
30852     "byte",
30853     "kilobyte",
30854     "megabyte",
30855     "gigabyte",
30856     "terabyte",
30857     "petabyte"
30858 ];
30859 
30860 /**
30861  * Return the type of this measurement. Examples are "mass",
30862  * "length", "speed", etc. Measurements can only be converted
30863  * to measurements of the same type.<p>
30864  *
30865  * The type of the units is determined automatically from the
30866  * units. For example, the unit "grams" is type "mass". Use the
30867  * static call {@link Measurement.getAvailableUnits}
30868  * to find out what units this version of ilib supports.
30869  *
30870  * @return {string} the name of the type of this measurement
30871  */
30872 DigitalStorageUnit.prototype.getMeasure = function() {
30873 	return "digitalStorage";
30874 };
30875 
30876 /**
30877  * Localize the measurement to the commonly used measurement in that locale. For example
30878  * If a user's locale is "en-US" and the measurement is given as "60 kmh",
30879  * the formatted number should be automatically converted to the most appropriate
30880  * measure in the other system, in this case, mph. The formatted result should
30881  * appear as "37.3 mph".
30882  *
30883  * @param {string} locale current locale string
30884  * @returns {Measurement} a new instance that is converted to locale
30885  */
30886 DigitalStorageUnit.prototype.localize = function(locale) {
30887     return new DigitalStorageUnit({
30888         unit: this.unit,
30889         amount: this.amount
30890     });
30891 };
30892 
30893 /**
30894  * Scale the measurement unit to an acceptable level. The scaling
30895  * happens so that the integer part of the amount is as small as
30896  * possible without being below zero. This will result in the
30897  * largest units that can represent this measurement without
30898  * fractions. Measurements can only be scaled to other measurements
30899  * of the same type.
30900  *
30901  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30902  * or undefined if the system can be inferred from the current measure
30903  * @param {Object=} units mapping from the measurement system to the units to use
30904  * for this scaling. If this is not defined, this measurement type will use the
30905  * set of units that it knows about for the given measurement system
30906  * @return {Measurement} a new instance that is scaled to the
30907  * right level
30908  */
30909 DigitalStorageUnit.prototype.scale = function(measurementsystem, units) {
30910     var mSystem, systemName = this.getMeasurementSystem();
30911     if (units) {
30912         mSystem = units[systemName];
30913     } else {
30914         if (JSUtils.indexOf(DigitalStorageUnit.byteSystem, this.unit) > -1) {
30915             mSystem = DigitalStorageUnit.byteSystem;
30916         } else {
30917             mSystem = DigitalStorageUnit.bitSystem;
30918         }
30919     }
30920     
30921     return this.newUnit(this.scaleUnits(mSystem));
30922 };
30923 
30924 /**
30925  * Expand the current measurement such that any fractions of the current unit
30926  * are represented in terms of smaller units in the same system instead of fractions
30927  * of the current unit. For example, "6.25 feet" may be represented as
30928  * "6 feet 4 inches" instead. The return value is an array of measurements which
30929  * are progressively smaller until the smallest unit in the system is reached
30930  * or until there is a whole number of any unit along the way.
30931  *
30932  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
30933  * or undefined if the system can be inferred from the current measure
30934  * @param {Object=} units mapping from the measurement system to the units to use
30935  * for this scaling. If this is not defined, this measurement type will use the
30936  * set of units that it knows about for the given measurement system
30937  * @return {Array.<Measurement>} an array of new measurements in order from
30938  * the current units to the smallest units in the system which together are the
30939  * same measurement as this one
30940  */
30941 DigitalStorageUnit.prototype.expand = function(measurementsystem, units) {
30942     var mSystem, systemName = this.getMeasurementSystem();
30943     if (units) {
30944         mSystem = units[systemName];
30945     } else {
30946         if (this.unit in DigitalStorageUnit.byteSystem) {
30947             mSystem = DigitalStorageUnit.byteSystem;
30948         } else {
30949             mSystem = DigitalStorageUnit.bitSystem;
30950         }
30951     }
30952 
30953     return this.list(mSystem, DigitalStorageUnit.ratios).map(function(item) {
30954         return new DigitalStorageUnit(item);
30955     });
30956 };
30957 
30958 
30959 DigitalStorageUnit.aliases = {
30960     "bits": "bit",
30961     "bit": "bit",
30962     "Bits": "bit",
30963     "Bit": "bit",
30964     "byte": "byte",
30965     "bytes": "byte",
30966     "Byte": "byte",
30967     "Bytes": "byte",
30968     "kilobits": "kilobit",
30969     "Kilobits": "kilobit",
30970     "KiloBits": "kilobit",
30971     "kiloBits": "kilobit",
30972     "kilobit": "kilobit",
30973     "Kilobit": "kilobit",
30974     "kiloBit": "kilobit",
30975     "KiloBit": "kilobit",
30976     "kb": "kilobit",
30977     "Kb": "kilobit",
30978     "kilobyte": "kilobyte",
30979     "Kilobyte": "kilobyte",
30980     "kiloByte": "kilobyte",
30981     "KiloByte": "kilobyte",
30982     "kilobytes": "kilobyte",
30983     "Kilobytes": "kilobyte",
30984     "kiloBytes": "kilobyte",
30985     "KiloBytes": "kilobyte",
30986     "kB": "kilobyte",
30987     "KB": "kilobyte",
30988     "megabit": "megabit",
30989     "Megabit": "megabit",
30990     "megaBit": "megabit",
30991     "MegaBit": "megabit",
30992     "megabits": "megabit",
30993     "Megabits": "megabit",
30994     "megaBits": "megabit",
30995     "MegaBits": "megabit",
30996     "Mb": "megabit",
30997     "mb": "megabit",
30998     "megabyte": "megabyte",
30999     "Megabyte": "megabyte",
31000     "megaByte": "megabyte",
31001     "MegaByte": "megabyte",
31002     "megabytes": "megabyte",
31003     "Megabytes": "megabyte",
31004     "megaBytes": "megabyte",
31005     "MegaBytes": "megabyte",
31006     "MB": "megabyte",
31007     "mB": "megabyte",
31008     "gigabit": "gigabit",
31009     "Gigabit": "gigabit",
31010     "gigaBit": "gigabit",
31011     "GigaBit": "gigabit",
31012     "gigabits": "gigabit",
31013     "Gigabits": "gigabit",
31014     "gigaBits": "gigabyte",
31015     "GigaBits": "gigabit",
31016     "Gb": "gigabit",
31017     "gb": "gigabit",
31018     "gigabyte": "gigabyte",
31019     "Gigabyte": "gigabyte",
31020     "gigaByte": "gigabyte",
31021     "GigaByte": "gigabyte",
31022     "gigabytes": "gigabyte",
31023     "Gigabytes": "gigabyte",
31024     "gigaBytes": "gigabyte",
31025     "GigaBytes": "gigabyte",
31026     "GB": "gigabyte",
31027     "gB": "gigabyte",
31028     "terabit": "terabit",
31029     "Terabit": "terabit",
31030     "teraBit": "terabit",
31031     "TeraBit": "terabit",
31032     "terabits": "terabit",
31033     "Terabits": "terabit",
31034     "teraBits": "terabit",
31035     "TeraBits": "terabit",
31036     "tb": "terabit",
31037     "Tb": "terabit",
31038     "terabyte": "terabyte",
31039     "Terabyte": "terabyte",
31040     "teraByte": "terabyte",
31041     "TeraByte": "terabyte",
31042     "terabytes": "terabyte",
31043     "Terabytes": "terabyte",
31044     "teraBytes": "terabyte",
31045     "TeraBytes": "terabyte",
31046     "TB": "terabyte",
31047     "tB": "terabyte",
31048     "petabit": "petabit",
31049     "Petabit": "petabit",
31050     "petaBit": "petabit",
31051     "PetaBit": "petabit",
31052     "petabits": "petabit",
31053     "Petabits": "petabit",
31054     "petaBits": "petabit",
31055     "PetaBits": "petabit",
31056     "pb": "petabit",
31057     "Pb": "petabit",
31058     "petabyte": "petabyte",
31059     "Petabyte": "petabyte",
31060     "petaByte": "petabyte",
31061     "PetaByte": "petabyte",
31062     "petabytes": "petabyte",
31063     "Petabytes": "petabyte",
31064     "petaBytes": "petabyte",
31065     "PetaBytes": "petabyte",
31066     "PB": "petabyte",
31067     "pB": "petabyte"
31068 };
31069 
31070 (function() {
31071     DigitalStorageUnit.aliasesLower = {};
31072     for (var a in DigitalStorageUnit.aliases) {
31073         DigitalStorageUnit.aliasesLower[a.toLowerCase()] = DigitalStorageUnit.aliases[a];
31074     }
31075 })();
31076 
31077 /**
31078  * Convert a digitalStorage to another measure.
31079  * @static
31080  * @param to {string} unit to convert to
31081  * @param from {string} unit to convert from
31082  * @param digitalStorage {number} amount to be convert
31083  * @returns {number|undefined} the converted amount
31084  */
31085 DigitalStorageUnit.convert = function(to, from, digitalStorage) {
31086     from = Measurement.getUnitIdCaseInsensitive(DigitalStorageUnit, from) || from;
31087     to = Measurement.getUnitIdCaseInsensitive(DigitalStorageUnit, to) || to;
31088 	var fromRow = DigitalStorageUnit.ratios[from];
31089 	var toRow = DigitalStorageUnit.ratios[to];
31090 	if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31091 		return undefined;
31092 	}
31093 	var result = digitalStorage * fromRow[toRow[0]];
31094     return result;
31095 };
31096 
31097 /**
31098  * @private
31099  * @static
31100  */
31101 DigitalStorageUnit.getMeasures = function () {
31102     return Object.keys(DigitalStorageUnit.ratios);
31103 };
31104 
31105 //register with the factory method
31106 Measurement._constructors["digitalStorage"] = DigitalStorageUnit;
31107 
31108 
31109 /*< DigitalSpeedUnit.js */
31110 /*
31111  * DigitalSpeedUnit.js - Unit conversions for Digital Storage
31112  *
31113  * Copyright © 2018 JEDLSoft
31114  *
31115  * Licensed under the Apache License, Version 2.0 (the "License");
31116  * you may not use this file except in compliance with the License.
31117  * You may obtain a copy of the License at
31118  *
31119  *     http://www.apache.org/licenses/LICENSE-2.0
31120  *
31121  * Unless required by applicable law or agreed to in writing, software
31122  * distributed under the License is distributed on an "AS IS" BASIS,
31123  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31124  *
31125  * See the License for the specific language governing permissions and
31126  * limitations under the License.
31127  */
31128 
31129 /*
31130 !depends
31131 Measurement.js
31132 JSUtils.js
31133 */
31134 
31135 
31136 /**
31137  * @class
31138  * Create a new DigitalSpeed measurement instance. This measures the speed of
31139  * transfer of data.
31140  *
31141  * @constructor
31142  * @extends Measurement
31143  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
31144  * the construction of this instance
31145  */
31146 var DigitalSpeedUnit = function (options) {
31147     this.unit = "byte";
31148     this.amount = 0;
31149 
31150     this.ratios = DigitalSpeedUnit.ratios;
31151     this.aliases = DigitalSpeedUnit.aliases;
31152     this.aliasesLower = DigitalSpeedUnit.aliasesLower;
31153     this.systems = DigitalSpeedUnit.systems;
31154 
31155     this.parent.call(this, options);
31156 };
31157 
31158 DigitalSpeedUnit.prototype = new Measurement();
31159 DigitalSpeedUnit.prototype.parent = Measurement;
31160 DigitalSpeedUnit.prototype.constructor = DigitalSpeedUnit;
31161 
31162 DigitalSpeedUnit.ratios = {
31163     /*                       #    bps               Bps              kbps             kBps             Mbps              MBps              Gbps             GBps               Tbps              TBps              Pbps               PBps               Bph             kBph             MBph             GBph             TBph            PBph   */
31164     "bit-per-second":      [ 1,   1,                0.125,           0.0009765625,    1.220703125e-4,  9.536743164e-7,   1.192092896e-7,   9.313225746e-10, 1.164153218e-10,   9.094947017e-13,  1.136868377e-13,  8.881784197e-16,   1.110223025e-16,   450,            0.45,           4.5e-4,          4.5e-7,          4.5e-10,         4.5e-13         ],
31165     "byte-per-second":     [ 2,   8,                1,               0.0078125,       0.0009765625,    7.629394531e-6,   9.536743164e-7,   7.450580597e-9,  9.313225746e-10,   7.275957614e-12,  9.094947017e-13,  7.105427358e-15,   8.881784197e-16,   3600,           3.6,            3.6e-3,          3.6e-6,          3.6e-9,          3.6e-12         ],
31166     "kilobit-per-second":  [ 3,   1024,             128,             1,               0.125,           0.0009765625,     1.220703125e-4,   9.536743164e-7,  1.192092896e-7,    9.313225746e-10,  1.164153218e-10,  9.094947017e-13,   1.136868377e-13,   4.5e5,          450,            0.45,            4.5e-4,          4.5e-7,          4.5e-10         ],
31167     "kilobyte-per-second": [ 4,   8192,             1024,            8,               1,               0.0078125,        0.0009765625,     7.629394531e-6,  9.536743164e-7,    7.450580597e-9,   9.313225746e-10,  7.275957614e-12,   9.094947017e-13,   3.6e6,          3600,           3.6,             3.6e-3,          3.6e-6,          3.6e-9          ],
31168     "megabit-per-second":  [ 5,   1048576,          131072,          1024,            128,             1,                0.125,            0.0009765625,    1.220703125e-4,    9.536743164e-7,   1.192092896e-7,   9.313225746e-10,   1.164153218e-10,   4.5e8,          4.5e5,          450,             0.45,            4.5e-4,          4.5e-7          ],
31169     "megabyte-per-second": [ 6,   8388608,          1048576,         8192,            1024,            8,                1,                0.0078125,       0.0009765625,      7.629394531e-6,   9.536743164e-7,   7.450580597e-9,    9.313225746e-10,   3.6e9,          3.6e6,          3600,            3.6,             3.6e-3,          3.6e-6          ],
31170     "gigabit-per-second":  [ 7,   1073741824,       134217728,       1048576,         131072,          1024,             128,              1,               0.125,             0.0009765625,     1.220703125e-4,   9.536743164e-7,    1.192092896e-7,    4.5e11,         4.5e8,          4.5e5,           450,             0.45,            4.5e-4          ],
31171     "gigabyte-per-second": [ 8,   8589934592,       1073741824,      8388608,         1048576,         8192,             1024,             8,               1,                 0.0078125,        0.0009765625,     7.629394531e-6,    9.536743164e-7,    3.6e12,         3.6e9,          3.6e6,           3600,            3.6,             3.6e-3          ],
31172     "terabit-per-second":  [ 9,   1.099511628e12,   137438953472,    1073741824,      134217728,       1048576,          131072,           1024,            128,               1,                0.125,            0.0009765625,      1.220703125e-4,    4.5e14,         4.5e11,         4.5e8,           4.5e5,           450,             0.45            ],
31173     "terabyte-per-second": [ 10,  8.796093022e12,   1.099511628e12,  8589934592,      1073741824,      8388608,          1048576,          8192,            1024,              8,                1,                0.0078125,         0.0009765625,      3.6e15,         3.6e12,         3.6e9,           3.6e6,           3600,            3.6             ],
31174     "petabit-per-second":  [ 11,  1.125899907e15,   1.407374884e14,  1.099511628e12,  137438953472,    1073741824,       134217728,        1048576,         131072,            1024,             128,              1,                 0.125,             4.5e17,         4.5e14,         4.5e11,          4.5e8,           4.5e5,           450             ],
31175     "petabyte-per-second": [ 12,  9.007199255e15,   1.125899907e15,  8.796093022e12,  1.099511628e12,  8589934592,       1073741824,       8388608,         1048576,           8192,             1024,             8,                 1,                 3.6e18,         3.6e15,         3.6e12,          3.6e9,           3.6e6,           3600            ],
31176 
31177     "byte-per-hour":       [ 13,  28800,            3600,            28.125,           3.515625,        0.0274658203116, 3.43322753904e-3, 2.68220901492e-5, 3.35276126856e-6, 2.61934474104e-8, 3.27418092612e-9, 2.55795384888e-11, 3.19744231092e-12, 1,              0.0078125,      9.536743164e-7,  9.313225746e-10, 9.094947017e-13, 8.881784197e-16 ],
31178     "kilobyte-per-hour":   [ 14,  29491200,         3686400,         28800,            3600,            28.125,          3.515625,         0.0274658203116,  3.43322753904e-3, 2.68220901492e-5, 3.35276126856e-6, 2.61934474104e-8,  3.27418092612e-9,  1024,           1,              0.0078125,       9.536743164e-7,  9.313225746e-10, 9.094947017e-13 ],
31179     "megabyte-per-hour":   [ 15,  30198988800,      3774873600,      29491200,         3686400,         28800,           3600,             28.125,           3.515625,         0.0274658203116,  3.43322753904e-3, 2.68220901492e-5,  3.35276126856e-6,  1048576,        1024,           1,               0.0078125,       9.536743164e-7,  9.313225746e-10 ],
31180     "gigabyte-per-hour":   [ 16,  30923764531200,   3865470566400,   30198988800,      3774873600,      29491200,        3686400,          28800,            3600,             28.125,           3.515625,         0.0274658203116,   3.43322753904e-3,  1073741824,     1048576,        1024,            1,               0.0078125,       9.536743164e-7  ],
31181     "terabyte-per-hour":   [ 17,  3.16659348792e16, 3.9582418608e16, 30923764531200,   3865470566400,   30198988800,     3774873600,       29491200,         3686400,          28800,            3600,             28.125,            3.515625,          1.099511628e12, 1073741824,     1048576,         1024,            1,               0.0078125       ],
31182     "petabyte-per-hour":   [ 18,  3.2425917318e19,  4.0532396652e18, 3.16659348792e16, 3.9582418608e16, 30923764531200,  3865470566400,    30198988800,      3774873600,       29491200,         3686400,          28800,             3600,              1.125899907e15, 1.099511628e12, 1073741824,      1048576,         1024,            1               ]
31183 };
31184 
31185 /**
31186  * Return a new instance of this type of measurement.
31187  *
31188  * @param {Object} params parameters to the constructor
31189  * @return {Measurement} a measurement subclass instance
31190  */
31191 DigitalSpeedUnit.prototype.newUnit = function(params) {
31192     return new DigitalSpeedUnit(params);
31193 };
31194 
31195 DigitalSpeedUnit.systems = {
31196     "metric": [],
31197     "uscustomary": [],
31198     "imperial": [],
31199     "conversions": {
31200         "metric": {},
31201         "uscustomary": {},
31202         "imperial": {}
31203     }
31204 };
31205 
31206 DigitalSpeedUnit.bitSystem = [
31207     "bit-per-second",
31208     "kilobit-per-second",
31209     "megabit-per-second",
31210     "gigabit-per-second",
31211     "terabit-per-second",
31212     "petabit-per-second"
31213 ];
31214 DigitalSpeedUnit.byteSystem = [
31215     "byte-per-second",
31216     "kilobyte-per-second",
31217     "megabyte-per-second",
31218     "gigabyte-per-second",
31219     "terabyte-per-second",
31220     "petabyte-per-second"
31221 ];
31222 
31223 /**
31224  * Return the type of this measurement. Examples are "mass",
31225  * "length", "speed", etc. Measurements can only be converted
31226  * to measurements of the same type.<p>
31227  *
31228  * The type of the units is determined automatically from the
31229  * units. For example, the unit "grams" is type "mass". Use the
31230  * static call {@link Measurement.getAvailableUnits}
31231  * to find out what units this version of ilib supports.
31232  *
31233  * @override
31234  * @return {string} the name of the type of this measurement
31235  */
31236 DigitalSpeedUnit.prototype.getMeasure = function() {
31237     return "digitalSpeed";
31238 };
31239 
31240 /**
31241  * Localize the measurement to the commonly used measurement in that locale. For example
31242  * If a user's locale is "en-US" and the measurement is given as "60 kmh",
31243  * the formatted number should be automatically converted to the most appropriate
31244  * measure in the other system, in this case, mph. The formatted result should
31245  * appear as "37.3 mph".
31246  *
31247  * @param {string} locale current locale string
31248  * @returns {Measurement} a new instance that is converted to locale
31249  */
31250 DigitalSpeedUnit.prototype.localize = function(locale) {
31251     return new DigitalSpeedUnit({
31252         unit: this.unit,
31253         amount: this.amount
31254     });
31255 };
31256 
31257 /**
31258  * Scale the measurement unit to an acceptable level. The scaling
31259  * happens so that the integer part of the amount is as small as
31260  * possible without being below zero. This will result in the
31261  * largest units that can represent this measurement without
31262  * fractions. Measurements can only be scaled to other measurements
31263  * of the same type.
31264  *
31265  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
31266  * or undefined if the system can be inferred from the current measure
31267  * @param {Object=} units mapping from the measurement system to the units to use
31268  * for this scaling. If this is not defined, this measurement type will use the
31269  * set of units that it knows about for the given measurement system
31270  * @return {Measurement} a new instance that is scaled to the
31271  * right level
31272  */
31273 DigitalSpeedUnit.prototype.scale = function(measurementsystem, units) {
31274     var mSystem, systemName = this.getMeasurementSystem();
31275     if (units) {
31276         mSystem = units[systemName];
31277     } else {
31278         if (JSUtils.indexOf(DigitalSpeedUnit.byteSystem, this.unit) > -1) {
31279             mSystem = DigitalSpeedUnit.byteSystem;
31280         } else {
31281             mSystem = DigitalSpeedUnit.bitSystem;
31282         }
31283     }
31284 
31285     return this.newUnit(this.scaleUnits(mSystem));
31286 };
31287 
31288 /**
31289  * Expand the current measurement such that any fractions of the current unit
31290  * are represented in terms of smaller units in the same system instead of fractions
31291  * of the current unit. For example, "6.25 feet" may be represented as
31292  * "6 feet 4 inches" instead. The return value is an array of measurements which
31293  * are progressively smaller until the smallest unit in the system is reached
31294  * or until there is a whole number of any unit along the way.
31295  *
31296  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
31297  * or undefined if the system can be inferred from the current measure
31298  * @param {Object=} units mapping from the measurement system to the units to use
31299  * for this scaling. If this is not defined, this measurement type will use the
31300  * set of units that it knows about for the given measurement system
31301  * @return {Array.<Measurement>} an array of new measurements in order from
31302  * the current units to the smallest units in the system which together are the
31303  * same measurement as this one
31304  */
31305 DigitalSpeedUnit.prototype.expand = function(measurementsystem, units) {
31306     var mSystem, systemName = this.getMeasurementSystem();
31307     if (units) {
31308         mSystem = units[systemName];
31309     } else {
31310         if (this.unit in DigitalSpeedUnit.byteSystem) {
31311             mSystem = DigitalSpeedUnit.byteSystem;
31312         } else {
31313             mSystem = DigitalSpeedUnit.bitSystem;
31314         }
31315     }
31316 
31317     return this.list(mSystem, DigitalSpeedUnit.ratios).map(function(item) {
31318         return new DigitalSpeedUnit(item);
31319     });
31320 };
31321 
31322 DigitalSpeedUnit.aliases = {
31323     "bits/s": "bit-per-second",
31324     "bit/s": "bit-per-second",
31325     "bits/second": "bit-per-second",
31326     "bit/second": "bit-per-second",
31327     "bps": "bit-per-second",
31328     "byte/s": "byte-per-second",
31329     "bytes/s": "byte-per-second",
31330     "byte/second": "byte-per-second",
31331     "bytes/second": "byte-per-second",
31332     "Bps": "byte-per-second",
31333     "kilobits/s": "kilobit-per-second",
31334     "kilobits/second": "kilobit-per-second",
31335     "Kilobits/s": "kilobit-per-second",
31336     "kilobit/s": "kilobit-per-second",
31337     "kilobit/second": "kilobit-per-second",
31338     "Kilobit/s": "kilobit-per-second",
31339     "kb/s": "kilobit-per-second",
31340     "Kb/s": "kilobit-per-second",
31341     "kbps": "kilobit-per-second",
31342     "Kbps": "kilobit-per-second",
31343     "kilobyte/s": "kilobyte-per-second",
31344     "kilobyte/second": "kilobyte-per-second",
31345     "Kilobyte/s": "kilobyte-per-second",
31346     "kilobytes/s": "kilobyte-per-second",
31347     "kilobytes/second": "kilobyte-per-second",
31348     "Kilobytes/s": "kilobyte-per-second",
31349     "kB/s": "kilobyte-per-second",
31350     "KB/s": "kilobyte-per-second",
31351     "kBps": "kilobyte-per-second",
31352     "KBps": "kilobyte-per-second",
31353     "megabit/s": "megabit-per-second",
31354     "megabits/s": "megabit-per-second",
31355     "megabit/second": "megabit-per-second",
31356     "megabits/second": "megabit-per-second",
31357     "Mb/s": "megabit-per-second",
31358     "mb/s": "megabit-per-second",
31359     "mbps": "megabit-per-second",
31360     "Mbps": "megabit-per-second",
31361     "megabyte/s": "megabyte-per-second",
31362     "megabytes/s": "megabyte-per-second",
31363     "megabyte/second": "megabyte-per-second",
31364     "megabytes/second": "megabyte-per-second",
31365     "MB/s": "megabyte-per-second",
31366     "mB/s": "megabyte-per-second",
31367     "mBps": "megabyte-per-second",
31368     "MBps": "megabyte-per-second",
31369     "gigabit/s": "gigabit-per-second",
31370     "gigabits/s": "gigabit-per-second",
31371     "gigabit/second": "gigabit-per-second",
31372     "gigabits/second": "gigabit-per-second",
31373     "Gb/s": "gigabit-per-second",
31374     "gb/s": "gigabit-per-second",
31375     "gbps": "gigabit-per-second",
31376     "Gbps": "gigabit-per-second",
31377     "gigabyte/s": "gigabyte-per-second",
31378     "gigabytes/s": "gigabyte-per-second",
31379     "gigabyte/second": "gigabyte-per-second",
31380     "gigabytes/second": "gigabyte-per-second",
31381     "GB/s": "gigabyte-per-second",
31382     "gB/s": "gigabyte-per-second",
31383     "gBps": "gigabyte-per-second",
31384     "GBps": "gigabyte-per-second",
31385     "terabit/second": "terabit-per-second",
31386     "terabits/second": "terabit-per-second",
31387     "tb/s": "terabit-per-second",
31388     "Tb/s": "terabit-per-second",
31389     "tbps": "terabit-per-second",
31390     "Tbps": "terabit-per-second",
31391     "terabyte/s": "terabyte-per-second",
31392     "terabytes/s": "terabyte-per-second",
31393     "terabyte/second": "terabyte-per-second",
31394     "terabytes/second": "terabyte-per-second",
31395     "TB/s": "terabyte-per-second",
31396     "tB/s": "terabyte-per-second",
31397     "tBps": "terabyte-per-second",
31398     "TBps": "terabyte-per-second",
31399     "petabit/s": "petabit-per-second",
31400     "petabits/s": "petabit-per-second",
31401     "petabit/second": "petabit-per-second",
31402     "petabits/second": "petabit-per-second",
31403     "pb/s": "petabit-per-second",
31404     "Pb/s": "petabit-per-second",
31405     "pbps": "petabit-per-second",
31406     "Pbps": "petabit-per-second",
31407     "petabyte/s": "petabyte-per-second",
31408     "petabytes/s": "petabyte-per-second",
31409     "petabyte/second": "petabyte-per-second",
31410     "petabytes/second": "petabyte-per-second",
31411     "PB/s": "petabyte-per-second",
31412     "pB/s": "petabyte-per-second",
31413     "pBps": "petabyte-per-second",
31414     "PBps": "petabyte-per-second",
31415     "byte/h": "byte-per-hour",
31416     "bytes/h": "byte-per-hour",
31417     "byte/hour": "byte-per-hour",
31418     "bytes/hour": "byte-per-hour",
31419     "B/h": "byte-per-hour",
31420     "Bph": "byte-per-hour",
31421     "kilobyte/h": "kilobyte-per-hour",
31422     "kilobytes/h": "kilobyte-per-hour",
31423     "kilobyte/hour": "kilobyte-per-hour",
31424     "kilobytes/hour": "kilobyte-per-hour",
31425     "kB/h": "kilobyte-per-hour",
31426     "KB/h": "kilobyte-per-hour",
31427     "kBph": "kilobyte-per-hour",
31428     "KBph": "kilobyte-per-hour",
31429     "megabyte/h": "megabyte-per-hour",
31430     "megabytes/h": "megabyte-per-hour",
31431     "megabyte/hour": "megabyte-per-hour",
31432     "megabytes/hour": "megabyte-per-hour",
31433     "MB/h": "megabyte-per-hour",
31434     "MBph": "megabyte-per-hour",
31435     "gigabyte/h": "gigabyte-per-hour",
31436     "gigabytes/h": "gigabyte-per-hour",
31437     "gigabyte/hour": "gigabyte-per-hour",
31438     "gigabytes/hour": "gigabyte-per-hour",
31439     "GB/h": "gigabyte-per-hour",
31440     "GBph": "gigabyte-per-hour",
31441     "petabyte/h": "petabyte-per-hour",
31442     "petabytes/h": "petabyte-per-hour",
31443     "petabyte/hour": "petabyte-per-hour",
31444     "petabytes/hour": "petabyte-per-hour",
31445     "PB/h": "petabyte-per-hour",
31446     "PBph": "petabyte-per-hour"
31447 };
31448 
31449 (function() {
31450     DigitalSpeedUnit.aliasesLower = {};
31451     for (var a in DigitalSpeedUnit.aliases) {
31452         DigitalSpeedUnit.aliasesLower[a.toLowerCase()] = DigitalSpeedUnit.aliases[a];
31453     }
31454 })();
31455 
31456 /**
31457  * Convert a digitalSpeed to another measure.
31458  * @static
31459  * @param to {string} unit to convert to
31460  * @param from {string} unit to convert from
31461  * @param digitalSpeed {number} amount to be convert
31462  * @returns {number|undefined} the converted amount
31463  */
31464 DigitalSpeedUnit.convert = function(to, from, digitalSpeed) {
31465     from = Measurement.getUnitIdCaseInsensitive(DigitalSpeedUnit, from) || from;
31466     to = Measurement.getUnitIdCaseInsensitive(DigitalSpeedUnit, to) || to;
31467     var fromRow = DigitalSpeedUnit.ratios[from];
31468     var toRow = DigitalSpeedUnit.ratios[to];
31469     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31470         return undefined;
31471     }
31472     var result = digitalSpeed * fromRow[toRow[0]];
31473     return result;
31474 };
31475 
31476 /**
31477  * @private
31478  * @static
31479  */
31480 DigitalSpeedUnit.getMeasures = function () {
31481     return Object.keys(DigitalSpeedUnit.ratios);
31482 };
31483 
31484 //register with the factory method
31485 Measurement._constructors["digitalSpeed"] = DigitalSpeedUnit;
31486 
31487 
31488 /*< EnergyUnit.js */
31489 /*
31490  * EnergyUnit.js - Unit conversions for energy measurements
31491  *
31492  * Copyright © 2014-2015, 2018 JEDLSoft
31493  *
31494  * Licensed under the Apache License, Version 2.0 (the "License");
31495  * you may not use this file except in compliance with the License.
31496  * You may obtain a copy of the License at
31497  *
31498  *     http://www.apache.org/licenses/LICENSE-2.0
31499  *
31500  * Unless required by applicable law or agreed to in writing, software
31501  * distributed under the License is distributed on an "AS IS" BASIS,
31502  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31503  *
31504  * See the License for the specific language governing permissions and
31505  * limitations under the License.
31506  */
31507 
31508 /*
31509 !depends
31510 Measurement.js
31511 */
31512 
31513 
31514 /**
31515  * @class
31516  * Create a new energy measurement instance.
31517  *
31518  * @constructor
31519  * @extends Measurement
31520  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
31521  * the construction of this instance
31522  */
31523 var EnergyUnit = function (options) {
31524     this.unit = "joule";
31525     this.amount = 0;
31526 
31527     this.ratios = EnergyUnit.ratios;
31528     this.aliases = EnergyUnit.aliases;
31529     this.aliasesLower = EnergyUnit.aliasesLower;
31530     this.systems = EnergyUnit.systems;
31531 
31532     this.parent.call(this, options);
31533 };
31534 
31535 EnergyUnit.prototype = new Measurement();
31536 EnergyUnit.prototype.parent = Measurement;
31537 EnergyUnit.prototype.constructor = EnergyUnit;
31538 
31539 EnergyUnit.ratios = {
31540    /*                index mJ          J           BTU               kJ          Wh                Cal               MJ             kWh                gJ             MWh                 GWh         */
31541     "millijoule":   [ 1,   1,          0.001,      9.4781707775e-7,  1e-6,       2.7777777778e-7,  2.3884589663e-7,  1.0e-9,        2.7777777778e-10,  1.0e-12,       2.7777777778e-13,   2.7777777778e-16  ],
31542     "joule":        [ 2,   1000,       1,          9.4781707775e-4,  0.001,      2.7777777778e-4,  2.3884589663e-4,  1.0e-6,        2.7777777778e-7,   1.0e-9,        2.7777777778e-10,   2.7777777778e-13  ],
31543     "BTU":          [ 3,   1055055.9,  1055.0559,  1,                1.0550559,  0.29307108333,    0.25199577243,    1.0550559e-3,  2.9307108333e-4,   1.0550559e-6,  2.9307108333e-7,    2.9307108333e-10  ],
31544     "kilojoule":    [ 4,   1000000,    1000,       0.94781707775,    1,          0.27777777778,    0.23884589663,    0.001,         2.7777777778e-4,   1.0e-6,        2.7777777778e-7,    2.7777777778e-10  ],
31545     "watt-hour":    [ 5,   3.6e+6,     3600,       3.4121414799,     3.6,        1,                0.85984522786,    0.0036,        0.001,             3.6e-6,        1.0e-6,             1.0e-9            ],
31546     "foodcalorie":  [ 6,   4.868e+5,   4186.8,     3.9683205411,     4.1868,     1.163,            1,                4.1868e-3,     1.163e-3,          4.1868e-6,     1.163e-6,           1.163e-9          ],
31547     "megajoule":    [ 7,   1e+9,       1e+6,       947.81707775,     1000,       277.77777778,     238.84589663,     1,             0.27777777778,     0.001,         2.7777777778e-4,    2.7777777778e-7   ],
31548     "kilowatt-hour":[ 8,   3.6e+9,     3.6e+6,     3412.1414799,     3600,       1000,             859.84522786,     3.6,           1,                 3.6e-3,        0.001,              1e-6              ],
31549     "gigajoule":    [ 9,   1e+12,      1e+9,       947817.07775,     1e+6,       277777.77778,     238845.89663,     1000,          277.77777778,      1,             0.27777777778,      2.7777777778e-4   ],
31550     "megawatt-hour":[ 10,  3.6e+12,    3.6e+9,     3412141.4799,     3.6e+6,     1e+6,             859845.22786,     3600,          1000,              3.6,           1,                  0.001             ],
31551     "gigawatt-hour":[ 11,  3.6e+15,    3.6e+12,    3412141479.9,     3.6e+9,     1e+9,             859845227.86,     3.6e+6,        1e+6,              3600,          1000,               1                 ]
31552 };
31553 
31554 /**
31555  * Return the type of this measurement. Examples are "mass",
31556  * "length", "speed", etc. Measurements can only be converted
31557  * to measurements of the same type.<p>
31558  *
31559  * The type of the units is determined automatically from the
31560  * units. For example, the unit "grams" is type "mass". Use the
31561  * static call {@link Measurement.getAvailableUnits}
31562  * to find out what units this version of ilib supports.
31563  *
31564  * @return {string} the name of the type of this measurement
31565  */
31566 EnergyUnit.prototype.getMeasure = function() {
31567 	return "energy";
31568 };
31569 
31570 /**
31571  * Return a new instance of this type of measurement.
31572  *  
31573  * @param {Object} params parameters to the constructor
31574  * @return {Measurement} a measurement subclass instance
31575  */
31576 EnergyUnit.prototype.newUnit = function(params) {
31577     return new EnergyUnit(params);
31578 };
31579 
31580 EnergyUnit.aliases = {
31581     "milli joule": "millijoule",
31582     "millijoule": "millijoule",
31583     "milliJ": "millijoule",
31584     "mJ": "millijoule",
31585     "joule": "joule",
31586     "joules": "joule",
31587     "J": "joule",
31588     "BTU": "BTU",
31589     "British Thermal Unit": "BTU",
31590     "British Thermal Units": "BTU",
31591     "kilo joule": "kilojoule",
31592     "kilojoule": "kilojoule",
31593     "kilojoules": "kilojoule",
31594     "kjoule": "kilojoule",
31595     "kJ": "kilojoule",
31596     "watt hour": "watt-hour",
31597     "watt hours": "watt-hour",
31598     "Wh": "watt-hour",
31599     "food calorie": "foodcalorie",
31600     "food calories": "foodcalorie",
31601     "calorie": "foodcalorie",
31602     "calories": "foodcalorie",
31603     "Cal": "foodcalorie",
31604     "mega joule": "megajoule",
31605     "mega joules": "megajoule",
31606     "megajoule": "megajoule",
31607     "megajoules": "megajoule",
31608     "MJ": "megajoule",
31609     "kilo watt hour": "kilowatt-hour",
31610     "kilo watt hours": "kilowatt-hour",
31611     "kiloWh": "kilowatt-hour",
31612     "kilowatt hour": "kilowatt-hour",
31613     "kilowatt hours": "kilowatt-hour",
31614     "kilowatt-hour": "kilowatt-hour",
31615     "kilowatt-hours": "kilowatt-hour",
31616     "kilowatthour": "kilowatt-hour",
31617     "kilowatthours": "kilowatt-hour",
31618     "kW hour": "kilowatt-hour",
31619     "kW hours": "kilowatt-hour",
31620     "kW-hour": "kilowatt-hour",
31621     "kW-hours": "kilowatt-hour",
31622     "kWh": "kilowatt-hour",
31623     "giga joule": "gigajoule",
31624     "Gj": "gigajoule",
31625     "gigajoule": "gigajoule",
31626     "gigajoules": "gigajoule",
31627     "mega watt hour": "megawatt-hour",
31628     "mega watt hours": "megawatt-hour",
31629     "megawatt hour": "megawatt-hour",
31630     "megawatt hours": "megawatt-hour",
31631     "megawatt-hour": "megawatt-hour",
31632     "megawatt-hours": "megawatt-hour",
31633     "MW hour": "megawatt-hour",
31634     "MW hours": "megawatt-hour",
31635     "MW-hour": "megawatt-hour",
31636     "MW-hours": "megawatt-hour",
31637     "megaWh": "megawatt-hour",
31638     "MWh": "megawatt-hour",
31639     "giga watt hour": "gigawatt-hour",
31640     "giga watt hours": "gigawatt-hour",
31641     "gigawatt hour": "gigawatt-hour",
31642     "gigawatt hours": "gigawatt-hour",
31643     "gigawatt-hours": "gigawatt-hour",
31644     "gigawatthour": "gigawatt-hour",
31645     "GW hour": "gigawatt-hour",
31646     "GW hours": "gigawatt-hour",
31647     "GW-hour": "gigawatt-hour",
31648     "GW-hours": "gigawatt-hour",
31649     "gigaWh": "gigawatt-hour",
31650     "GWh": "gigawatt-hour"
31651 };
31652 
31653 (function() {
31654     EnergyUnit.aliasesLower = {};
31655     for (var a in EnergyUnit.aliases) {
31656         EnergyUnit.aliasesLower[a.toLowerCase()] = EnergyUnit.aliases[a];
31657     }
31658 })();
31659 
31660 /**
31661  * Convert a energy to another measure.
31662  * @static
31663  * @param to {string} unit to convert to
31664  * @param from {string} unit to convert from
31665  * @param energy {number} amount to be convert
31666  * @returns {number|undefined} the converted amount
31667  */
31668 EnergyUnit.convert = function(to, from, energy) {
31669     from = Measurement.getUnitIdCaseInsensitive(EnergyUnit, from) || from;
31670     to = Measurement.getUnitIdCaseInsensitive(EnergyUnit, to) || to;
31671     var fromRow = EnergyUnit.ratios[from];
31672     var toRow = EnergyUnit.ratios[to];
31673     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31674         return undefined;
31675     }
31676     return energy * fromRow[toRow[0]];
31677 };
31678 
31679 EnergyUnit.systems = {
31680     "metric": [
31681         "millijoule",
31682         "joule",
31683         "kilojoule",
31684         "watt-hour",
31685         "megajoule",
31686         "kilowatt-hour",
31687         "gigajoule",
31688         "megawatt-hour",
31689         "gigawatt-hour"
31690     ],
31691     "imperial": [
31692         "BTU",
31693         "foodcalorie"
31694     ],
31695     "uscustomary": [
31696         "BTU",
31697         "foodcalorie"
31698     ],
31699     "conversions": {
31700         "metric": {
31701             "uscustomary": {
31702                 "millijoule": "BTU",
31703                 "joule": "BTU",
31704                 "kilojoule": "BTU",
31705                 "watt-hour": "BTU",
31706                 "megajoule": "BTU",
31707                 "kilowatt-hour": "BTU",
31708                 "gigajoule": "BTU",
31709                 "megawatt-hour": "BTU",
31710                 "gigawatt-hour": "BTU"
31711             },
31712             "imperial": {
31713                 "millijoule": "BTU",
31714                 "joule": "BTU",
31715                 "kilojoule": "BTU",
31716                 "watt-hour": "BTU",
31717                 "megajoule": "BTU",
31718                 "kilowatt-hour": "BTU",
31719                 "gigajoule": "BTU",
31720                 "megawatt-hour": "BTU",
31721                 "gigawatt-hour": "BTU"
31722             }
31723         },
31724         "uscustomary": {
31725             "metric": {
31726                 "BTU": "joule",
31727                 "foodcalorie": "joule"
31728             }
31729         },
31730         "imperial": {
31731             "metric": {
31732                 "BTU": "joule",
31733                 "foodcalorie": "joule"
31734             }
31735         }
31736     }
31737 };
31738 
31739 /**
31740  * @private
31741  * @static
31742  */
31743 EnergyUnit.getMeasures = function () {
31744     return Object.keys(EnergyUnit.ratios);
31745 };
31746 
31747 //register with the factory method
31748 Measurement._constructors["energy"] = EnergyUnit;
31749 
31750 
31751 /*< FuelConsumptionUnit.js */
31752 /*
31753  * FuelConsumptionUnit.js - Unit conversions for fuel consumption measurements
31754  *
31755  * Copyright © 2014-2015, 2018 JEDLSoft
31756  *
31757  * Licensed under the Apache License, Version 2.0 (the "License");
31758  * you may not use this file except in compliance with the License.
31759  * You may obtain a copy of the License at
31760  *
31761  *     http://www.apache.org/licenses/LICENSE-2.0
31762  *
31763  * Unless required by applicable law or agreed to in writing, software
31764  * distributed under the License is distributed on an "AS IS" BASIS,
31765  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31766  *
31767  * See the License for the specific language governing permissions and
31768  * limitations under the License.
31769  */
31770 
31771 /*
31772 !depends
31773 Measurement.js
31774 */
31775 
31776 
31777 /**
31778  * @class
31779  * Create a new fuelconsumption measurement instance.
31780  *
31781  * @constructor
31782  * @extends Measurement
31783  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
31784  * the construction of this instance
31785  */
31786 var FuelConsumptionUnit = function(options) {
31787     this.unit = "liter-per-100kilometers";
31788     this.amount = 0;
31789 
31790     this.ratios = FuelConsumptionUnit.ratios;
31791     this.aliases = FuelConsumptionUnit.aliases;
31792     this.aliasesLower = FuelConsumptionUnit.aliasesLower;
31793     this.systems = FuelConsumptionUnit.systems;
31794 
31795     this.parent.call(this, options);
31796 };
31797 
31798 FuelConsumptionUnit.prototype = new Measurement();
31799 FuelConsumptionUnit.prototype.parent = Measurement;
31800 FuelConsumptionUnit.prototype.constructor = FuelConsumptionUnit;
31801 
31802 FuelConsumptionUnit.ratios = {
31803     /*                         index    km/L        L/km        L/100km           mpg         mpgi       inverse? */
31804      "kilometer-per-liter":      [ 1,   1,          1,          100,              2.35215,    2.82481,   false ],
31805      "liter-per-kilometer":      [ 2,   1,          1,          0.01,             2.35215,    2.82481,   true  ],
31806      "liter-per-100kilometers":  [ 3,   100,        0.01,       1,                235.215,    282.481,   true  ],
31807      "mile-per-gallon":          [ 4,   0.425144,   2.35215,    235.215,          1,          1.20095,   false ],
31808      "mile-per-gallon-imperial": [ 5,   0.354006,   2.82481,    282.481,          0.8326741,  1,         false ]
31809 };
31810 
31811 /**
31812  * Return the type of this measurement. Examples are "mass",
31813  * "length", "speed", etc. Measurements can only be converted
31814  * to measurements of the same type.<p>
31815  *
31816  * The type of the units is determined automatically from the
31817  * units. For example, the unit "grams" is type "mass". Use the
31818  * static call {@link Measurement.getAvailableUnits}
31819  * to find out what units this version of ilib supports.
31820  *
31821  * @return {string} the name of the type of this measurement
31822  */
31823 FuelConsumptionUnit.prototype.getMeasure = function() {
31824     return "fuelconsumption";
31825 };
31826 
31827 /**
31828  * Return a new instance of this type of measurement.
31829  *
31830  * @param {Object} params parameters to the constructor
31831  * @return {Measurement} a measurement subclass instance
31832  */
31833 FuelConsumptionUnit.prototype.newUnit = function(params) {
31834     return new FuelConsumptionUnit(params);
31835 };
31836 
31837 FuelConsumptionUnit.aliases = {
31838     "Km/liter": "kilometer-per-liter",
31839     "KM/Liter": "kilometer-per-liter",
31840     "KM/L": "kilometer-per-liter",
31841     "Kilometers Per Liter": "kilometer-per-liter",
31842     "kilometers per liter": "kilometer-per-liter",
31843     "km/l": "kilometer-per-liter",
31844     "Kilometers/Liter": "kilometer-per-liter",
31845     "Kilometer/Liter": "kilometer-per-liter",
31846     "kilometers/liter": "kilometer-per-liter",
31847     "kilometer/liter": "kilometer-per-liter",
31848     "km/liter": "kilometer-per-liter",
31849     "Liter/100km": "liter-per-100kilometers",
31850     "Liters/100km": "liter-per-100kilometers",
31851     "Liter/100kms": "liter-per-100kilometers",
31852     "Liters/100kms": "liter-per-100kilometers",
31853     "liter/100km": "liter-per-100kilometers",
31854     "liters/100kms": "liter-per-100kilometers",
31855     "liters/100km": "liter-per-100kilometers",
31856     "liter/100kms": "liter-per-100kilometers",
31857     "Liter/100KM": "liter-per-100kilometers",
31858     "Liters/100KM": "liter-per-100kilometers",
31859     "L/100km": "liter-per-100kilometers",
31860     "L/100KM": "liter-per-100kilometers",
31861     "l/100KM": "liter-per-100kilometers",
31862     "l/100km": "liter-per-100kilometers",
31863     "l/100kms": "liter-per-100kilometers",
31864     "Liter/km": "liter-per-kilometer",
31865     "Liters/km": "liter-per-kilometer",
31866     "Liter/kms": "liter-per-kilometer",
31867     "Liters/kms": "liter-per-kilometer",
31868     "liter/km": "liter-per-kilometer",
31869     "liters/kms": "liter-per-kilometer",
31870     "liters/km": "liter-per-kilometer",
31871     "liter/kms": "liter-per-kilometer",
31872     "Liter/KM": "liter-per-kilometer",
31873     "Liters/KM": "liter-per-kilometer",
31874     "L/km": "liter-per-kilometer",
31875     "L/KM": "liter-per-kilometer",
31876     "l/KM": "liter-per-kilometer",
31877     "l/km": "liter-per-kilometer",
31878     "l/kms": "liter-per-kilometer",
31879     "MPG(US)": "mile-per-gallon",
31880     "USMPG ": "mile-per-gallon",
31881     "mpg": "mile-per-gallon",
31882     "mpgUS": "mile-per-gallon",
31883     "mpg(US)": "mile-per-gallon",
31884     "mpg(us)": "mile-per-gallon",
31885     "mpg-us": "mile-per-gallon",
31886     "mpg Imp": "mile-per-gallon-imperial",
31887     "MPG(imp)": "mile-per-gallon-imperial",
31888     "mpg(imp)": "mile-per-gallon-imperial",
31889     "mpg-imp": "mile-per-gallon-imperial"
31890 };
31891 
31892 (function() {
31893     FuelConsumptionUnit.aliasesLower = {};
31894     for (var a in FuelConsumptionUnit.aliases) {
31895         FuelConsumptionUnit.aliasesLower[a.toLowerCase()] = FuelConsumptionUnit.aliases[a];
31896     }
31897 })();
31898 
31899 /**
31900  * Return a new measurement instance that is converted to a new
31901  * measurement unit. Measurements can only be converted
31902  * to measurements of the same type.<p>
31903  *
31904  * @param {string} to The name of the units to convert to
31905  * @return {number|undefined} the converted measurement
31906  * or undefined if the requested units are for a different
31907  * measurement type
31908  */
31909 FuelConsumptionUnit.prototype.convert = function(to) {
31910     if (!to || typeof(FuelConsumptionUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
31911         return undefined;
31912     }
31913     return FuelConsumptionUnit.convert(to, this.unit, this.amount);
31914 };
31915 
31916 FuelConsumptionUnit.systems = {
31917     "metric": [
31918         "liter-per-kilometer",
31919         "liter-per-100kilometers",
31920         "kilometer-per-liter"
31921     ],
31922     "uscustomary": [
31923         "mile-per-gallon"
31924     ],
31925     "imperial": [
31926         "mile-per-gallon-imperial"
31927     ],
31928     "conversions": {
31929         "metric": {
31930             "uscustomary": {
31931                 "liter-per-kilometer": "mile-per-gallon",
31932                 "kilometer-per-liter": "mile-per-gallon",
31933                 "liter-per-100kilometers": "mile-per-gallon"
31934             },
31935             "imperial": {
31936                 "liter-per-kilometer": "mile-per-gallon-imperial",
31937                 "kilometer-per-liter": "mile-per-gallon-imperial",
31938                 "liter-per-100kilometers": "mile-per-gallon-imperial"
31939             }
31940         },
31941         "uscustomary": {
31942             "metric": {
31943                 "mile-per-gallon": "liter-per-100kilometers"
31944             },
31945             "imperial": {
31946                 "mile-per-gallon": "mile-per-gallon-imperial"
31947             }
31948         },
31949         "imperial": {
31950             "metric": {
31951                 "mile-per-gallon-imperial": "liter-per-100kilometers"
31952             },
31953             "uscustomary": {
31954                 "mile-per-gallon-imperial": "mile-per-gallon"
31955             }
31956         }
31957     }
31958 };
31959 
31960 /**
31961  * Convert a FuelConsumption to another measure.
31962  *
31963  * @static
31964  * @param to {string} unit to convert to
31965  * @param from {string} unit to convert from
31966  * @param fuelConsumption {number} amount to be convert
31967  * @returns {number|undefined} the converted amount
31968  */
31969 FuelConsumptionUnit.convert = function(to, from, fuelConsumption) {
31970     from = Measurement.getUnitIdCaseInsensitive(FuelConsumptionUnit, from) || from;
31971     to = Measurement.getUnitIdCaseInsensitive(FuelConsumptionUnit, to) || to;
31972     var fromRow = FuelConsumptionUnit.ratios[from];
31973     var toRow = FuelConsumptionUnit.ratios[to];
31974     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
31975         return undefined;
31976     }
31977 
31978     if (fromRow[6] !== toRow[6]) {
31979         // inverses of each other. Avoid the divide by 0.
31980         return fuelConsumption ? (fromRow[toRow[0]] / fuelConsumption) : 0;
31981     }
31982 
31983     // not inverses, so just multiply by the factor
31984     return fuelConsumption * fromRow[toRow[0]];
31985 };
31986 
31987 /**
31988  * Scale the measurement unit to an acceptable level. The scaling
31989  * happens so that the integer part of the amount is as small as
31990  * possible without being below zero. This will result in the
31991  * largest units that can represent this measurement without
31992  * fractions. Measurements can only be scaled to other measurements
31993  * of the same type.
31994  *
31995  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
31996  * or undefined if the system can be inferred from the current measure
31997  * @param {Object=} units mapping from the measurement system to the units to use
31998  * for this scaling. If this is not defined, this measurement type will use the
31999  * set of units that it knows about for the given measurement system
32000  * @return {Measurement} a new instance that is scaled to the
32001  * right level
32002  */
32003 FuelConsumptionUnit.prototype.scale = function(measurementsystem, units) {
32004     return new FuelConsumptionUnit({
32005         unit: this.unit,
32006         amount: this.amount
32007     });
32008 };
32009 
32010 /**
32011  * Expand the current measurement such that any fractions of the current unit
32012  * are represented in terms of smaller units in the same system instead of fractions
32013  * of the current unit. For example, "6.25 feet" may be represented as
32014  * "6 feet 4 inches" instead. The return value is an array of measurements which
32015  * are progressively smaller until the smallest unit in the system is reached
32016  * or until there is a whole number of any unit along the way.
32017  *
32018  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
32019  * or undefined if the system can be inferred from the current measure
32020  * @param {Object=} units mapping from the measurement system to the units to use
32021  * for this scaling. If this is not defined, this measurement type will use the
32022  * set of units that it knows about for the given measurement system
32023  * @return {Array.<Measurement>} an array of new measurements in order from
32024  * the current units to the smallest units in the system which together are the
32025  * same measurement as this one
32026  */
32027 FuelConsumptionUnit.prototype.expand = function(measurementsystem, units) {
32028     return [this]; // nothing to expand
32029 };
32030 
32031 /**
32032  * @private
32033  * @static
32034  */
32035 FuelConsumptionUnit.getMeasures = function() {
32036     return Object.keys(FuelConsumptionUnit.ratios);
32037 };
32038 
32039 //register with the factory method
32040 Measurement._constructors["fuelconsumption"] = FuelConsumptionUnit;
32041 
32042 
32043 /*< LengthUnit.js */
32044 /*
32045  * LengthUnit.js - Unit conversions for length measurements
32046  *
32047  * Copyright © 2014-2015, 2018 JEDLSoft
32048  *
32049  * Licensed under the Apache License, Version 2.0 (the "License");
32050  * you may not use this file except in compliance with the License.
32051  * You may obtain a copy of the License at
32052  *
32053  *     http://www.apache.org/licenses/LICENSE-2.0
32054  *
32055  * Unless required by applicable law or agreed to in writing, software
32056  * distributed under the License is distributed on an "AS IS" BASIS,
32057  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32058  *
32059  * See the License for the specific language governing permissions and
32060  * limitations under the License.
32061  */
32062 
32063 /*
32064 !depends
32065 Measurement.js
32066 */
32067 
32068 
32069 /**
32070  * @class
32071  * Create a new length measurement instance.
32072  *
32073  * @constructor
32074  * @extends Measurement
32075  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
32076  * the construction of this instance
32077  */
32078 var LengthUnit = function (options) {
32079     this.unit = "meter";
32080     this.amount = 0;
32081 
32082     this.ratios = LengthUnit.ratios;
32083     this.aliases = LengthUnit.aliases;
32084     this.aliasesLower = LengthUnit.aliasesLower;
32085     this.systems = LengthUnit.systems;
32086 
32087     this.parent.call(this, options);
32088 };
32089 
32090 LengthUnit.prototype = new Measurement();
32091 LengthUnit.prototype.parent = Measurement;
32092 LengthUnit.prototype.constructor = LengthUnit;
32093 
32094 LengthUnit.ratios = {
32095     /*               index, µm           mm           cm           inch         dm           foot          yard          m             dam            hm              km              mile            nm            Mm             Gm             */
32096     "micrometer":    [ 1,   1,           1e-3,        1e-4,        3.93701e-5,  1e-5,        3.28084e-6,   1.09361e-6,   1e-6,         1e-7,          1e-8,           1e-9,           6.21373e-10,  5.39957e-10,  1e-12,          1e-15           ],
32097     "millimeter":    [ 2,   1000,        1,           0.1,         0.0393701,   0.01,        0.00328084,   1.09361e-3,   0.001,        1e-4,          1e-5,           1e-6,           6.21373e-7,   5.39957e-7,   1e-9,           1e-12           ],
32098     "centimeter":    [ 3,   1e4,         10,          1,           0.393701,    0.1,         0.0328084,    0.0109361,    0.01,         0.001,         1e-4,           1e-5,           6.21373e-6,   5.39957e-6,   1e-8,           1e-9            ],
32099     "inch":          [ 4,   25399.986,   25.399986,   2.5399986,   1,           0.25399986,  0.083333333,  0.027777778,  0.025399986,  2.5399986e-3,  2.5399986e-4,   2.5399986e-5,   1.5783e-5,    1.3715e-5,    2.5399986e-8,   2.5399986e-11   ],
32100     "decimeter":     [ 5,   1e5,         100,         10,          3.93701,     1,           0.328084,     0.109361,     0.1,          0.01,          0.001,          1e-4,           6.21373e-5,   5.39957e-5,   1e-7,           1e-8            ],
32101     "foot":          [ 6,   304799.99,   304.79999,   30.479999,   12,          3.0479999,   1,            0.33333333,   0.30479999,   0.030479999,   3.0479999e-3,   3.0479999e-4,   1.89394e-4,   1.64579e-4,   3.0479999e-7,   3.0479999e-10   ],
32102     "yard":          [ 7,   914402.758,  914.402758,  91.4402758,  36,          9.14402758,  3,            1,            0.914402758,  0.0914402758,  9.14402758e-3,  9.14402758e-4,  5.68182e-4,   4.93737e-4,   9.14402758e-7,  9.14402758e-10  ],
32103     "meter":         [ 8,   1e6,         1000,        100,         39.3701,     10,          3.28084,      1.09361,      1,            0.1,           0.01,           0.001,          6.213712e-4,  5.39957e-4,   1e-6,           1e-7            ],
32104     "decameter":     [ 9,   1e7,         1e4,         1000,        393.701,     100,         32.8084,      10.9361,      10,           1,             0.1,            0.01,           6.21373e-3,   5.39957e-3,   1e-5,           1e-6            ],
32105     "hectometer":    [ 10,  1e8,         1e5,         1e4,         3937.01,     1000,        328.084,      109.361,      100,          10,            1,              0.1,            0.0621373,    0.0539957,    1e-4,           1e-5            ],
32106     "kilometer":     [ 11,  1e9,         1e6,         1e5,         39370.1,     1e4,         3280.84,      1093.61,      1000,         100,           10,             1,              0.621373,     0.539957,     0.001,          1e-4            ],
32107     "mile":          [ 12,  1.60934e9,   1.60934e6,   1.60934e5,   63360,       1.60934e4,   5280,         1760,         1609.34,      160.934,       16.0934,        1.60934,        1,            0.868976,     1.60934e-3,     1.60934e-6      ],
32108     "nautical-mile": [ 13,  1.852e9,     1.852e6,     1.852e5,     72913.4,     1.852e4,     6076.12,      2025.37,      1852,         185.2,         18.52,          1.852,          1.15078,      1,            1.852e-3,       1.852e-6        ],
32109     "megameter":     [ 14,  1e12,        1e9,         1e6,         3.93701e7,   1e5,         3.28084e6,    1.09361e6,    1e4,          1000,          100,            10,             621.373,      539.957,      1,              0.001           ],
32110     "gigameter":     [ 15,  1e15,        1e12,        1e9,         3.93701e10,  1e8,         3.28084e9,    1.09361e9,    1e7,          1e6,           1e5,            1e4,            621373.0,     539957.0,     1000,           1               ]
32111 };
32112 
32113 /**
32114  * Return a new instance of this type of measurement.
32115  *
32116  * @param {Object} params parameters to the constructor
32117  * @return {Measurement} a measurement subclass instance
32118  */
32119 LengthUnit.prototype.newUnit = function(params) {
32120     return new LengthUnit(params);
32121 };
32122 
32123 LengthUnit.systems = {
32124     "metric": [
32125         "micrometer",
32126         "millimeter",
32127         "centimeter",
32128         "decimeter",
32129         "meter",
32130         "decameter",
32131         "hectometer",
32132         "kilometer",
32133         "megameter",
32134         "gigameter"
32135     ],
32136     "imperial": [
32137         "inch",
32138         "foot",
32139         "yard",
32140         "mile",
32141         "nautical-mile"
32142     ],
32143     "uscustomary": [
32144         "inch",
32145         "foot",
32146         "yard",
32147         "mile",
32148         "nautical-mile"
32149     ],
32150     "conversions": {
32151         "metric": {
32152             "uscustomary": {
32153                 "micrometer": "inch",
32154                 "millimeter": "inch",
32155                 "centimeter": "inch",
32156                 "decimeter": "inch",
32157                 "meter": "yard",
32158                 "decameter": "yard",
32159                 "hectometer": "mile",
32160                 "kilometer": "mile",
32161                 "megameter": "mile",
32162                 "gigameter": "mile"
32163             },
32164             "imperial": {
32165                 "micrometer": "inch",
32166                 "millimeter": "inch",
32167                 "centimeter": "inch",
32168                 "decimeter": "inch",
32169                 "meter": "yard",
32170                 "decameter": "yard",
32171                 "hectometer": "mile",
32172                 "kilometer": "mile",
32173                 "megameter": "mile",
32174                 "gigameter": "mile"
32175             }
32176         },
32177         "uscustomary": {
32178             "metric": {
32179                 "inch": "centimeter",
32180                 "foot": "centimeter",
32181                 "yard": "meter",
32182                 "mile": "kilometer",
32183                 "nautical-mile": "kilometer"
32184             }
32185         },
32186         "imperial": {
32187             "metric": {
32188                 "inch": "centimeter",
32189                 "foot": "centimeter",
32190                 "yard": "meter",
32191                 "mile": "kilometer",
32192                 "nautical-mile": "kilometer"
32193             }
32194         }
32195     }
32196 };
32197 
32198 /**
32199  * Return the type of this measurement. Examples are "mass",
32200  * "length", "speed", etc. Measurements can only be converted
32201  * to measurements of the same type.<p>
32202  *
32203  * The type of the units is determined automatically from the
32204  * units. For example, the unit "grams" is type "mass". Use the
32205  * static call {@link Measurement.getAvailableUnits}
32206  * to find out what units this version of ilib supports.
32207  *
32208  * @returns {string} the name of the type of this measurement
32209  */
32210 LengthUnit.prototype.getMeasure = function() {
32211     return "length";
32212 };
32213 
32214 LengthUnit.aliases = {
32215     "miles": "mile",
32216     "mile":"mile",
32217     "nauticalmiles": "nautical-mile",
32218     "nautical mile": "nautical-mile",
32219     "nautical miles": "nautical-mile",
32220     "nauticalmile":"nautical-mile",
32221     "yards": "yard",
32222     "yard": "yard",
32223     "feet": "foot",
32224     "foot": "foot",
32225     "inches": "inch",
32226     "inch": "inch",
32227     "in": "inch",
32228     "meters": "meter",
32229     "metre": "meter",
32230     "metres": "meter",
32231     "m": "meter",
32232     "meter": "meter",
32233     "micrometers": "micrometer",
32234     "micrometres": "micrometer",
32235     "micrometre": "micrometer",
32236     "µm": "micrometer",
32237     "micrometer": "micrometer",
32238     "millimeters": "millimeter",
32239     "millimetres": "millimeter",
32240     "millimetre": "millimeter",
32241     "mm": "millimeter",
32242     "millimeter": "millimeter",
32243     "centimeters": "centimeter",
32244     "centimetres": "centimeter",
32245     "centimetre": "centimeter",
32246     "cm": "centimeter",
32247     "centimeter": "centimeter",
32248     "decimeters": "decimeter",
32249     "decimetres": "decimeter",
32250     "decimetre": "decimeter",
32251     "dm": "decimeter",
32252     "decimeter": "decimeter",
32253     "decameters": "decameter",
32254     "decametres": "decameter",
32255     "decametre": "decameter",
32256     "dam": "decameter",
32257     "decameter": "decameter",
32258     "hectometers": "hectometer",
32259     "hectometres": "hectometer",
32260     "hectometre": "hectometer",
32261     "hm": "hectometer",
32262     "hectometer": "hectometer",
32263     "kilometers": "kilometer",
32264     "kilometres": "kilometer",
32265     "kilometre": "kilometer",
32266     "km": "kilometer",
32267     "kilometer": "kilometer",
32268     "megameters": "megameter",
32269     "megametres": "megameter",
32270     "megametre": "megameter",
32271     "Mm": "megameter",
32272     "megameter": "megameter",
32273     "gigameters": "gigameter",
32274     "gigametres": "gigameter",
32275     "gigametre": "gigameter",
32276     "Gm": "gigameter",
32277     "gigameter": "gigameter"
32278 };
32279 
32280 (function() {
32281     LengthUnit.aliasesLower = {};
32282     for (var a in LengthUnit.aliases) {
32283         LengthUnit.aliasesLower[a.toLowerCase()] = LengthUnit.aliases[a];
32284     }
32285 })();
32286 
32287 /**
32288  * Convert a length to another measure.
32289  * @static
32290  * @param to {string} unit to convert to
32291  * @param from {string} unit to convert from
32292  * @param length {number} amount to be convert
32293  * @returns {number|undefined} the converted amount
32294  */
32295 LengthUnit.convert = function(to, from, length) {
32296     from = Measurement.getUnitIdCaseInsensitive(LengthUnit, from) || from;
32297     to = Measurement.getUnitIdCaseInsensitive(LengthUnit, to) || to;
32298     var fromRow = LengthUnit.ratios[from];
32299     var toRow = LengthUnit.ratios[to];
32300     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
32301         return undefined;
32302     }
32303     return length * fromRow[toRow[0]];
32304 };
32305 
32306 /**
32307  * @private
32308  * @static
32309  */
32310 LengthUnit.getMeasures = function () {
32311     return Object.keys(LengthUnit.ratios);
32312 };
32313 
32314 //register with the factory method
32315 Measurement._constructors["length"] = LengthUnit;
32316 
32317 
32318 /*< MassUnit.js */
32319 /*
32320  * MassUnit.js - Unit conversions for weight/mass measurements
32321  *
32322  * Copyright © 2014-2015, 2018 JEDLSoft
32323  *
32324  * Licensed under the Apache License, Version 2.0 (the "License");
32325  * you may not use this file except in compliance with the License.
32326  * You may obtain a copy of the License at
32327  *
32328  *     http://www.apache.org/licenses/LICENSE-2.0
32329  *
32330  * Unless required by applicable law or agreed to in writing, software
32331  * distributed under the License is distributed on an "AS IS" BASIS,
32332  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32333  *
32334  * See the License for the specific language governing permissions and
32335  * limitations under the License.
32336  */
32337 
32338 /*
32339 !depends
32340 Measurement.js
32341 */
32342 
32343 
32344 /**
32345  * @class
32346  * Create a new mass measurement instance.
32347  *
32348  * @constructor
32349  * @extends Measurement
32350  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
32351  * the construction of this instance
32352  */
32353 var MassUnit = function (options) {
32354 	this.unit = "gram";
32355 	this.amount = 0;
32356 
32357     this.ratios = MassUnit.ratios;
32358     this.aliases = MassUnit.aliases;
32359     this.aliasesLower = MassUnit.aliasesLower;
32360     this.systems = MassUnit.systems;
32361 
32362     this.parent.call(this, options);
32363 };
32364 
32365 MassUnit.prototype = new Measurement();
32366 MassUnit.prototype.parent = Measurement;
32367 MassUnit.prototype.constructor = MassUnit;
32368 
32369 MassUnit.ratios = {
32370     /*             index  µg          mg         g          oz          lb           kg          st            sh ton       mt ton        ln ton      */
32371     "microgram":   [ 1,   1,          0.001,     1e-6,      3.5274e-8,  2.2046e-9,   1e-9,       1.5747e-10,   1.1023e-12,  1e-12,        9.8421e-13   ],
32372     "milligram":   [ 2,   1000,       1,         0.001,     3.5274e-5,  2.2046e-6,   1e-6,       1.5747e-7,    1.1023e-9,   1e-9,         9.8421e-10   ],
32373     "gram":        [ 3,   1e+6,       1000,      1,         0.035274,   0.00220462,  0.001,      0.000157473,  1.1023e-6,   1e-6,         9.8421e-7    ],
32374     "ounce":       [ 4,   2.835e+7,   28349.5,   28.3495,   1,          0.0625,      0.0283495,  0.00446429,   3.125e-5,    2.835e-5,     2.7902e-5    ],
32375     "pound":       [ 5,   4.536e+8,   453592,    453.592,   16,         1,           0.453592,   0.0714286,    0.0005,      0.000453592,  0.000446429  ],
32376     "kilogram":    [ 6,   1e+9,       1e+6,      1000,      35.274,     2.20462,     1,          0.157473,     0.00110231,  0.001,        0.000984207  ],
32377     "stone":       [ 7,   6.35e+9,    6.35e+6,   6350.29,   224,        14,          6.35029,    1,            0.007,       0.00635029,   0.00625      ],
32378     "short-ton":   [ 8,   9.072e+11,  9.072e+8,  907185,    32000,      2000,        907.185,    142.857,      1,           0.907185,     0.892857     ],
32379     "metric-ton":  [ 9,   1e+12,      1e+9,      1e+6,      35274,      2204.62,     1000,       157.473,      1.10231,     1,            0.984207     ],
32380     "long-ton":    [ 10,  1.016e+12,  1.016e+9,  1.016e+6,  35840,      2240,        1016.05,    160,          1.12,        1.01605,      1            ]
32381 };
32382 
32383 /**
32384  * Return a new instance of this type of measurement.
32385  *
32386  * @param {Object} params parameters to the constructor
32387  * @return {Measurement} a measurement subclass instance
32388  */
32389 MassUnit.prototype.newUnit = function(params) {
32390     return new MassUnit(params);
32391 };
32392 
32393 MassUnit.systems = {
32394     "metric": [
32395         "microgram",
32396         "milligram",
32397         "gram",
32398         "kilogram",
32399         "metric-ton"
32400     ],
32401     "imperial": [
32402         "ounce",
32403         "pound",
32404         "stone",
32405         "long-ton"
32406     ],
32407     "uscustomary": [
32408         "ounce",
32409         "pound",
32410         "short-ton"
32411     ],
32412     "conversions": {
32413         "metric": {
32414             "uscustomary": {
32415                 "microgram": "ounce",
32416                 "milligram": "ounce",
32417                 "gram": "ounce",
32418                 "kilogram": "pound",
32419                 "metric-ton": "short-ton"
32420             },
32421             "imperial": {
32422                 "microgram": "ounce",
32423                 "milligram": "ounce",
32424                 "gram": "ounce",
32425                 "kilogram": "pound",
32426                 "metric-ton": "long-ton"
32427             }
32428         },
32429         "uscustomary": {
32430             "imperial": {
32431                 "ounce": "ounce",
32432                 "pound": "pound",
32433                 "short-ton": "long-ton"
32434             },
32435             "metric": {
32436                 "ounce": "gram",
32437                 "pound": "kilogram",
32438                 "short-ton": "metric-ton"
32439             }
32440         },
32441         "imperial": {
32442             "uscustomary": {
32443                 "ounce": "ounce",
32444                 "pound": "pound",
32445                 "stone": "pound",
32446                 "long-ton": "short-ton"
32447             },
32448             "metric": {
32449                 "ounce": "gram",
32450                 "pound": "kilogram",
32451                 "stone": "kilogram",
32452                 "long-ton": "metric-ton"
32453             }
32454         }
32455     }
32456 };
32457 
32458 /**
32459  * Return the type of this measurement. Examples are "mass",
32460  * "length", "speed", etc. Measurements can only be converted
32461  * to measurements of the same type.<p>
32462  *
32463  * The type of the units is determined automatically from the
32464  * units. For example, the unit "grams" is type "mass". Use the
32465  * static call {@link Measurement.getAvailableUnits}
32466  * to find out what units this version of ilib supports.
32467  *
32468  * @return {string} the name of the type of this measurement
32469  */
32470 MassUnit.prototype.getMeasure = function() {
32471 	return "mass";
32472 };
32473 
32474 MassUnit.aliases = {
32475     "µg":"microgram",
32476     "microgram":"microgram",
32477     "mcg":"microgram",
32478     "milligram":"milligram",
32479     "mg":"milligram",
32480     "milligrams":"milligram",
32481     "Milligram":"milligram",
32482     "Milligrams":"milligram",
32483     "MilliGram":"milligram",
32484     "MilliGrams":"milligram",
32485     "g":"gram",
32486     "gram":"gram",
32487     "grams":"gram",
32488     "Gram":"gram",
32489     "Grams":"gram",
32490     "ounce":"ounce",
32491     "oz":"ounce",
32492     "Ounce":"ounce",
32493     "ounces":"ounce",
32494     "Ounces":"ounce",
32495     "℥":"ounce",
32496     "pound":"pound",
32497     "poundm":"pound",
32498     "℔":"pound",
32499     "lb":"pound",
32500     "lbs":"pound",
32501     "pounds":"pound",
32502     "Pound":"pound",
32503     "Pounds":"pound",
32504     "kilogram":"kilogram",
32505     "kg":"kilogram",
32506     "kilograms":"kilogram",
32507     "kilo grams":"kilogram",
32508     "kilo gram":"kilogram",
32509     "Kilogram":"kilogram",
32510     "Kilograms":"kilogram",
32511     "KiloGram":"kilogram",
32512     "KiloGrams":"kilogram",
32513     "Kilo gram":"kilogram",
32514     "Kilo grams":"kilogram",
32515     "Kilo Gram":"kilogram",
32516     "Kilo Grams":"kilogram",
32517     "stone":"stone",
32518     "st":"stone",
32519     "stones":"stone",
32520     "Stone":"stone",
32521     "metric ton":"metric-ton",
32522     "metricton":"metric-ton",
32523     "t":"metric-ton",
32524     "tonne":"metric-ton",
32525     "tonnes":"metric-ton",
32526     "Tonne":"metric-ton",
32527     "Metric Ton":"metric-ton",
32528     "MetricTon":"metric-ton",
32529     "long ton":"long-ton",
32530     "longton":"long-ton",
32531     "Longton":"long-ton",
32532     "Long ton":"long-ton",
32533     "Long Ton":"long-ton",
32534     "short ton":"short-ton",
32535     "short tons":"short-ton",
32536     "Short ton":"short-ton",
32537     "Short Ton":"short-ton",
32538     "ton":"short-ton",
32539     "tons":"short-ton",
32540     "Ton":"short-ton"
32541 };
32542 
32543 (function() {
32544     MassUnit.aliasesLower = {};
32545     for (var a in MassUnit.aliases) {
32546         MassUnit.aliasesLower[a.toLowerCase()] = MassUnit.aliases[a];
32547     }
32548 })();
32549 
32550 /**
32551  * Convert a mass to another measure.
32552  * @static
32553  * @param to {string} unit to convert to
32554  * @param from {string} unit to convert from
32555  * @param mass {number} amount to be convert
32556  * @returns {number|undefined} the converted amount
32557  */
32558 MassUnit.convert = function(to, from, mass) {
32559     from = Measurement.getUnitIdCaseInsensitive(MassUnit, from) || from;
32560     to = Measurement.getUnitIdCaseInsensitive(MassUnit, to) || to;
32561     var fromRow = MassUnit.ratios[from];
32562     var toRow = MassUnit.ratios[to];
32563     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
32564         return undefined;
32565     }
32566     return mass * fromRow[toRow[0]];
32567 };
32568 
32569 /**
32570  * @private
32571  * @static
32572  */
32573 MassUnit.getMeasures = function () {
32574     return Object.keys(MassUnit.ratios);
32575 };
32576 
32577 //register with the factory method
32578 Measurement._constructors["mass"] = MassUnit;
32579 
32580 
32581 /*< TemperatureUnit.js */
32582 /*
32583  * TemperatureUnit.js - Unit conversions for temperature measurements
32584  *
32585  * Copyright © 2014-2015, 2018 JEDLSoft
32586  *
32587  * Licensed under the Apache License, Version 2.0 (the "License");
32588  * you may not use this file except in compliance with the License.
32589  * You may obtain a copy of the License at
32590  *
32591  *     http://www.apache.org/licenses/LICENSE-2.0
32592  *
32593  * Unless required by applicable law or agreed to in writing, software
32594  * distributed under the License is distributed on an "AS IS" BASIS,
32595  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32596  *
32597  * See the License for the specific language governing permissions and
32598  * limitations under the License.
32599  */
32600 
32601 /*
32602 !depends
32603 Measurement.js
32604 */
32605 
32606 
32607 /**
32608  * @class
32609  * Create a new Temperature measurement instance.
32610  *
32611  * @constructor
32612  * @extends Measurement
32613  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
32614  * the construction of this instance
32615  */
32616 var TemperatureUnit = function (options) {
32617     this.unit = "celsius";
32618     this.amount = 0;
32619 
32620     this.ratios = TemperatureUnit.ratios;
32621     this.aliases = TemperatureUnit.aliases;
32622     this.aliasesLower = TemperatureUnit.aliasesLower;
32623     this.systems = TemperatureUnit.systems;
32624 
32625     this.parent.call(this, options);
32626 };
32627 
32628 TemperatureUnit.prototype = new Measurement();
32629 TemperatureUnit.prototype.parent = Measurement;
32630 TemperatureUnit.prototype.constructor = TemperatureUnit;
32631 
32632 TemperatureUnit.ratios = {
32633     /*            index, C            K            F   */
32634     "celsius":    [ 1,   1,           1,           9/5 ],
32635     "kelvin":     [ 2,   1,           1,           9/5 ],
32636     "fahrenheit": [ 3,   5/9,         5/9,         1   ]
32637 };
32638 
32639 /**
32640  * Return the type of this measurement. Examples are "mass",
32641  * "length", "speed", etc. Measurements can only be converted
32642  * to measurements of the same type.<p>
32643  *
32644  * The type of the units is determined automatically from the
32645  * units. For example, the unit "grams" is type "mass". Use the
32646  * static call {@link Measurement.getAvailableUnits}
32647  * to find out what units this version of ilib supports.
32648  *
32649  * @return {string} the name of the type of this measurement
32650  */
32651 TemperatureUnit.prototype.getMeasure = function() {
32652 	return "temperature";
32653 };
32654 
32655 /**
32656  * Return a new instance of this type of measurement.
32657  *
32658  * @param {Object} params parameters to the constructor
32659  * @return {Measurement} a measurement subclass instance
32660  */
32661 TemperatureUnit.prototype.newUnit = function(params) {
32662     return new TemperatureUnit(params);
32663 };
32664 
32665 TemperatureUnit.systems = {
32666     "metric": [
32667         "celsius",
32668         "kelvin"
32669     ],
32670     "uscustomary": [
32671         "fahrenheit"
32672     ],
32673     "imperial": [
32674         "fahrenheit"
32675     ],
32676     "conversions": {
32677         "metric": {
32678             "uscustomary": {
32679                 "celsius": "fahrenheit",
32680                 "kelvin": "fahrenheit"
32681             },
32682             "imperial": {
32683                 "celsius": "fahrenheit",
32684                 "kelvin": "fahrenheit"
32685             }
32686         },
32687         "uscustomary": {
32688             "metric": {
32689                 "fahrenheit": "celsius"
32690             }
32691         },
32692         "imperial": {
32693             "metric": {
32694                 "fahrenheit": "celsius"
32695             }
32696         }
32697     }
32698 };
32699 
32700 TemperatureUnit.aliases = {
32701     "Celsius": "celsius",
32702     "C": "celsius",
32703     "Centegrade": "celsius",
32704     "Centigrade": "celsius",
32705     "Fahrenheit": "fahrenheit",
32706     "F": "fahrenheit",
32707     "K": "kelvin",
32708     "Kelvin": "kelvin",
32709     "°F": "fahrenheit",
32710     "℉": "fahrenheit",
32711     "℃": "celsius",
32712     "°C": "celsius"
32713 };
32714 
32715 (function() {
32716     TemperatureUnit.aliasesLower = {};
32717     for (var a in TemperatureUnit.aliases) {
32718         TemperatureUnit.aliasesLower[a.toLowerCase()] = TemperatureUnit.aliases[a];
32719     }
32720 })();
32721 
32722 /**
32723  * Return a new measurement instance that is converted to a new
32724  * measurement unit. Measurements can only be converted
32725  * to measurements of the same type.<p>
32726  *
32727  * @param {string} to The name of the units to convert to
32728  * @return {number|undefined} the converted measurement
32729  * or undefined if the requested units are for a different
32730  * measurement type
32731  */
32732 TemperatureUnit.prototype.convert = function(to) {
32733     if (!to || typeof(TemperatureUnit.ratios[this.normalizeUnits(to)]) === 'undefined') {
32734         return undefined;
32735     }
32736     return TemperatureUnit.convert(to, this.unit, this.amount);
32737 };
32738 
32739 /**
32740  * Convert a temperature to another measure.
32741  * @static
32742  * @param to {string} unit to convert to
32743  * @param from {string} unit to convert from
32744  * @param temperature {number} amount to be convert
32745  * @returns {number|undefined} the converted amount
32746  */
32747 TemperatureUnit.convert = function(to, from, temperature) {
32748     var result = 0;
32749     from = Measurement.getUnitIdCaseInsensitive(TemperatureUnit, from) || from;
32750     to = Measurement.getUnitIdCaseInsensitive(TemperatureUnit, to) || to;
32751     if (from === to) {
32752         return temperature;
32753     } else if (from === "celsius") {
32754         if (to === "fahrenheit") {
32755             result = ((temperature * 9 / 5) + 32);
32756         } else if (to === "kelvin") {
32757             result = (temperature + 273.15);
32758         }
32759     } else if (from === "fahrenheit") {
32760         if (to === "celsius") {
32761             result = ((5 / 9 * (temperature - 32)));
32762         } else if (to === "kelvin") {
32763             result = ((temperature + 459.67) * 5 / 9);
32764         }
32765     } else if (from === "kelvin") {
32766         if (to === "celsius") {
32767             result = (temperature - 273.15);
32768         } else if (to === "fahrenheit") {
32769             result = ((temperature * 9 / 5) - 459.67);
32770         }
32771     }
32772 
32773     return result;
32774 };
32775 
32776 /**
32777  * Scale the measurement unit to an acceptable level. The scaling
32778  * happens so that the integer part of the amount is as small as
32779  * possible without being below zero. This will result in the
32780  * largest units that can represent this measurement without
32781  * fractions. Measurements can only be scaled to other measurements
32782  * of the same type.
32783  *
32784  * @param {string=} measurementsystem system to use (uscustomary|imperial|metric),
32785  * or undefined if the system can be inferred from the current measure
32786  * @return {Measurement} a new instance that is scaled to the
32787  * right level
32788  */
32789 TemperatureUnit.prototype.scale = function(measurementsystem) {
32790     // no scaling for temp units
32791     return this;
32792  };
32793 
32794 /**
32795  * @private
32796  * @static
32797  */
32798 TemperatureUnit.getMeasures = function () {
32799     return ["celsius", "kelvin", "fahrenheit"];
32800 };
32801 
32802 //register with the factory method
32803 Measurement._constructors["temperature"] = TemperatureUnit;
32804 
32805 
32806 /*< TimeUnit.js */
32807 /*
32808  * TimeUnit.js - Unit conversions for time measurements
32809  *
32810  * Copyright © 2014-2015, 2018 JEDLSoft
32811  *
32812  * Licensed under the Apache License, Version 2.0 (the "License");
32813  * you may not use this file except in compliance with the License.
32814  * You may obtain a copy of the License at
32815  *
32816  *     http://www.apache.org/licenses/LICENSE-2.0
32817  *
32818  * Unless required by applicable law or agreed to in writing, software
32819  * distributed under the License is distributed on an "AS IS" BASIS,
32820  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
32821  *
32822  * See the License for the specific language governing permissions and
32823  * limitations under the License.
32824  */
32825 
32826 /*
32827 !depends
32828 Measurement.js
32829 */
32830 
32831 
32832 /**
32833  * @class
32834  * Create a new time measurement instance.
32835  *
32836  * @constructor
32837  * @extends Measurement
32838  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
32839  * the construction of this instance
32840  */
32841 var TimeUnit = function (options) {
32842     this.unit = "second";
32843     this.amount = 0;
32844 
32845     this.ratios = TimeUnit.ratios;
32846     this.aliases = TimeUnit.aliases;
32847     this.aliasesLower = TimeUnit.aliasesLower;
32848     this.systems = TimeUnit.systems;
32849 
32850     this.parent.call(this, options);
32851 };
32852 
32853 TimeUnit.prototype = new Measurement();
32854 TimeUnit.prototype.parent = Measurement;
32855 TimeUnit.prototype.constructor = TimeUnit;
32856 
32857 TimeUnit.ratios = {
32858     /*              index  nsec        msec        mlsec       sec        min          hour          day           week         month        year         decade        century      millenium */
32859     "nanosecond":   [ 1,   1,          0.001,      1e-6,       1e-9,      1.6667e-11,  2.7778e-13,   1.1574e-14,   1.6534e-15,  3.8027e-16,  3.1689e-17,  3.1689e-18,   3.1689e-19,  3.1689e-20],
32860     "microsecond":  [ 2,   1000,       1,          0.001,      1e-6,      1.6667e-8,   2.7778e-10,   1.1574e-11,   1.6534e-12,  3.8027e-13,  3.1689e-14,  3.1689e-15,   3.1689e-16,  3.1689e-17],
32861     "millisecond":  [ 3,   1e+6,       1000,       1,          0.001,     1.6667e-5,   2.7778e-7,    1.1574e-8,    1.6534e-9,   3.8027e-10,  3.1689e-11,  3.1689e-12,   3.1689e-13,  3.1689e-14],
32862     "second":       [ 4,   1e+9,       1e+6,       1000,       1,         0.0166667,   0.000277778,  1.1574e-5,    1.6534e-6,   3.8027e-7,   3.1689e-8,   3.1689e-9,    3.1689e-10,  3.1689e-11],
32863     "minute":       [ 5,   6e+10,      6e+7,       60000,      60,        1,           0.0166667,    0.000694444,  9.9206e-5,   2.2816e-5,   1.9013e-6,   1.9013e-7,    1.9013e-8,   1.9013e-9 ],
32864     "hour":         [ 6,   3.6e+12,    3.6e+9,     3.6e+6,     3600,      60,          1,            0.0416667,    0.00595238,  0.00136895,  0.00011408,  1.1408e-5,    1.1408e-6,   1.1408e-7 ],
32865     "day":          [ 7,   8.64e+13,   8.64e+10,   8.64e+7,    86400,     1440,        24,           1,            0.142857,    0.0328549,   0.00273791,  0.000273791,  2.7379e-5,   2.7379e-6 ],
32866     "week":         [ 8,   6.048e+14,  6.048e+11,  6.048e+8,   604800,    10080,       168,          7,            1,           0.229984,    0.0191654,   0.00191654,   0.000191654, 1.91654e-5],
32867     "month":        [ 9,   2.63e+15,   2.63e+12,   2.63e+9,    2.63e+6,   43829.1,     730.484,      30.4368,      4.34812,     1,           0.0833333,   0.00833333,   0.000833333, 8.33333e-5],
32868     "year":         [ 10,  3.156e+16,  3.156e+13,  3.156e+10,  3.156e+7,  525949,      8765.81,      365.242,      52.1775,     12,          1,           0.1,          0.01,        0.001     ],
32869     "decade":       [ 11,  3.156e+17,  3.156e+14,  3.156e+11,  3.156e+8,  5.259e+6,    87658.1,      3652.42,      521.775,     120,         10,          1,            0.1,         0.01      ],
32870     "century":      [ 12,  3.156e+18,  3.156e+18,  3.156e+12,  3.156e+9,  5.259e+7,    876581,       36524.2,      5217.75,     1200,        100,         10,           1,           0.1       ],
32871     "millenium":    [ 13,  3.156e+19,  3.156e+19,  3.156e+13,  3.156e+10, 5.259e+8,    8765810,      365242,       52177.5,     12000,       1000,        100,          10,          1         ]
32872 };
32873 
32874 /**
32875  * Return the type of this measurement. Examples are "mass",
32876  * "length", "speed", etc. Measurements can only be converted
32877  * to measurements of the same type.<p>
32878  *
32879  * The type of the units is determined automatically from the
32880  * units. For example, the unit "grams" is type "mass". Use the
32881  * static call {@link Measurement.getAvailableUnits}
32882  * to find out what units this version of ilib supports.
32883  *
32884  * @return {string} the name of the type of this measurement
32885  */
32886 TimeUnit.prototype.getMeasure = function() {
32887     return "time";
32888 };
32889 
32890 /**
32891  * Return a new instance of this type of measurement.
32892  *
32893  * @param {Object} params parameters to the constructor
32894  * @return {Measurement} a measurement subclass instance
32895  */
32896 TimeUnit.prototype.newUnit = function(params) {
32897     return new TimeUnit(params);
32898 };
32899 
32900 
32901 TimeUnit.systems = {
32902     "metric": [
32903         "nanosecond",
32904         "microsecond",
32905         "millisecond",
32906         "second",
32907         "minute",
32908         "hour",
32909         "day",
32910         "week",
32911         "month",
32912         "year",
32913         "decade",
32914         "century"
32915     ],
32916     "uscustomary": [
32917         "nanosecond",
32918         "microsecond",
32919         "millisecond",
32920         "second",
32921         "minute",
32922         "hour",
32923         "day",
32924         "week",
32925         "month",
32926         "year",
32927         "decade",
32928         "century"
32929     ],
32930     "imperial": [
32931         "nanosecond",
32932         "microsecond",
32933         "millisecond",
32934         "second",
32935         "minute",
32936         "hour",
32937         "day",
32938         "week",
32939         "month",
32940         "year",
32941         "decade",
32942         "century"
32943     ],
32944     "conversions": {
32945         "metric": {},
32946         "uscustomary": {},
32947         "imperial": {}
32948     }
32949 };
32950 
32951 TimeUnit.aliases = {
32952     "ns": "nanosecond",
32953     "NS": "nanosecond",
32954     "nS": "nanosecond",
32955     "Ns": "nanosecond",
32956     "Nanosecond": "nanosecond",
32957     "Nanoseconds": "nanosecond",
32958     "nanosecond": "nanosecond",
32959     "nanoseconds": "nanosecond",
32960     "NanoSecond": "nanosecond",
32961     "NanoSeconds": "nanosecond",
32962     "μs": "microsecond",
32963     "μS": "microsecond",
32964     "microsecond": "microsecond",
32965     "microseconds": "microsecond",
32966     "Microsecond": "microsecond",
32967     "Microseconds": "microsecond",
32968     "MicroSecond": "microsecond",
32969     "MicroSeconds": "microsecond",
32970     "ms": "millisecond",
32971     "MS": "millisecond",
32972     "mS": "millisecond",
32973     "Ms": "millisecond",
32974     "millisecond": "millisecond",
32975     "milliseconds": "millisecond",
32976     "Millisecond": "millisecond",
32977     "Milliseconds": "millisecond",
32978     "MilliSecond": "millisecond",
32979     "MilliSeconds": "millisecond",
32980     "s": "second",
32981     "S": "second",
32982     "sec": "second",
32983     "second": "second",
32984     "seconds": "second",
32985     "Second": "second",
32986     "Seconds": "second",
32987     "min": "minute",
32988     "Min": "minute",
32989     "minute": "minute",
32990     "minutes": "minute",
32991     "Minute": "minute",
32992     "Minutes": "minute",
32993     "h": "hour",
32994     "H": "hour",
32995     "hr": "hour",
32996     "Hr": "hour",
32997     "hR": "hour",
32998     "HR": "hour",
32999     "hour": "hour",
33000     "hours": "hour",
33001     "Hour": "hour",
33002     "Hours": "hour",
33003     "Hrs": "hour",
33004     "hrs": "hour",
33005     "day": "day",
33006     "days": "day",
33007     "Day": "day",
33008     "Days": "day",
33009     "week": "week",
33010     "weeks": "week",
33011     "Week": "week",
33012     "Weeks": "week",
33013     "month": "month",
33014     "Month": "month",
33015     "months": "month",
33016     "Months": "month",
33017     "year": "year",
33018     "years": "year",
33019     "Year": "year",
33020     "Years": "year",
33021     "yr": "year",
33022     "Yr": "year",
33023     "yrs": "year",
33024     "Yrs": "year",
33025     "decade": "decade",
33026     "decades": "decade",
33027     "Decade": "decade",
33028     "Decades": "decade",
33029     "century": "century",
33030     "centuries": "century",
33031     "Century": "century",
33032     "Centuries": "century",
33033     "millenium": "millenium",
33034     "milleniums": "millenium",
33035     "millenia": "millenium",
33036     "mill.": "millenium",
33037     "milm": "millenium"
33038 };
33039 
33040 (function() {
33041     TimeUnit.aliasesLower = {};
33042     for (var a in TimeUnit.aliases) {
33043         TimeUnit.aliasesLower[a.toLowerCase()] = TimeUnit.aliases[a];
33044     }
33045 })();
33046 
33047 /**
33048  * Convert a time to another measure.
33049  * @static
33050  * @param to {string} unit to convert to
33051  * @param from {string} unit to convert from
33052  * @param time {number} amount to be convert
33053  * @returns {number|undefined} the converted amount
33054  */
33055 TimeUnit.convert = function(to, from, time) {
33056     from = Measurement.getUnitIdCaseInsensitive(TimeUnit, from) || from;
33057     to = Measurement.getUnitIdCaseInsensitive(TimeUnit, to) || to;
33058     var fromRow = TimeUnit.ratios[from];
33059     var toRow = TimeUnit.ratios[to];
33060     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
33061         return undefined;
33062     }
33063     return time * fromRow[toRow[0]];
33064 };
33065 
33066 /**
33067  * @private
33068  * @static
33069  */
33070 TimeUnit.getMeasures = function () {
33071     return Object.keys(TimeUnit.ratios);
33072 };
33073 
33074 //register with the factory method
33075 Measurement._constructors["time"] = TimeUnit;
33076 
33077 
33078 /*< VelocityUnit.js */
33079 /*
33080  * VelocityUnit.js - Unit conversions for velocity/speed measurements
33081  *
33082  * Copyright © 2014-2015, 2018 JEDLSoft
33083  *
33084  * Licensed under the Apache License, Version 2.0 (the "License");
33085  * you may not use this file except in compliance with the License.
33086  * You may obtain a copy of the License at
33087  *
33088  *     http://www.apache.org/licenses/LICENSE-2.0
33089  *
33090  * Unless required by applicable law or agreed to in writing, software
33091  * distributed under the License is distributed on an "AS IS" BASIS,
33092  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33093  *
33094  * See the License for the specific language governing permissions and
33095  * limitations under the License.
33096  */
33097 
33098 /*
33099 !depends
33100 Measurement.js
33101 */
33102 
33103 
33104 /**
33105  * @class
33106  * Create a new speed measurement instance.
33107  *
33108  * @constructor
33109  * @extends Measurement
33110  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
33111  * the construction of this instance
33112  */
33113 var VelocityUnit = function (options) {
33114     this.unit = "meter-per-second";
33115     this.amount = 0;
33116 
33117     this.ratios = VelocityUnit.ratios;
33118     this.aliases = VelocityUnit.aliases;
33119     this.aliasesLower = VelocityUnit.aliasesLower;
33120     this.systems = VelocityUnit.systems;
33121 
33122     this.parent.call(this, options);
33123 };
33124 
33125 VelocityUnit.prototype = new Measurement();
33126 VelocityUnit.prototype.parent = Measurement;
33127 VelocityUnit.prototype.constructor = VelocityUnit;
33128 
33129 VelocityUnit.ratios = {
33130     /*                    index,  k/h         f/s         miles/h      knot         m/s        km/s         miles/s */
33131     "kilometer-per-hour":   [ 1,  1,          0.911344,   0.621371,    0.539957,    0.277778,  2.77778e-4,  1.72603109e-4 ],
33132     "foot-per-second":      [ 2,  1.09728,    1,          0.681818,    0.592484,    0.3048,    3.048e-4,    1.89393939e-4 ],
33133     "mile-per-hour":        [ 3,  1.60934,    1.46667,    1,           0.868976,    0.44704,   4.4704e-4,   2.77777778e-4 ],
33134     "knot":                 [ 4,  1.852,      1.68781,    1.15078,     1,           0.514444,  5.14444e-4,  3.19660958e-4 ],
33135     "meter-per-second":     [ 5,  3.6,        3.28084,    2.236936,    1.94384,     1,         0.001,       6.21371192e-4 ],
33136     "kilometer-per-second": [ 6,  3600,       3280.8399,  2236.93629,  1943.84449,  1000,      1,           0.621371192   ],
33137     "mile-per-second":      [ 7,  5793.6384,  5280,       3600,        3128.31447,  1609.344,  1.609344,    1             ]
33138 };
33139 
33140 /**
33141  * Return a new instance of this type of measurement.
33142  *
33143  * @param {Object} params parameters to the constructor
33144  * @return {Measurement} a measurement subclass instance
33145  */
33146 VelocityUnit.prototype.newUnit = function(params) {
33147     return new VelocityUnit(params);
33148 };
33149 
33150 VelocityUnit.systems = {
33151     "metric": [
33152         "kilometer-per-hour",
33153         "meter-per-second",
33154         "kilometer-per-second"
33155     ],
33156     "imperial": [
33157         "foot-per-second",
33158         "mile-per-hour",
33159         "knot",
33160         "mile-per-second"
33161     ],
33162     "uscustomary": [
33163         "foot-per-second",
33164         "mile-per-hour",
33165         "knot",
33166         "mile-per-second"
33167     ],
33168     "conversions": {
33169         "imperial": {
33170             "metric": {
33171                 "mile-per-hour": "kilometer-per-hour",
33172                 "foot-per-second": "meter-per-second",
33173                 "mile-per-second": "kilometer-per-second",
33174                 "knot": "kilometer-per-hour"
33175             }
33176         },
33177         "uscustomary": {
33178             "metric": {
33179                 "mile-per-hour": "kilometer-per-hour",
33180                 "foot-per-second": "meter-per-second",
33181                 "mile-per-second": "kilometer-per-second",
33182                 "knot": "kilometer-per-hour"
33183             }
33184         },
33185         "metric": {
33186             "uscustomary": {
33187                 "kilometer-per-hour": "mile-per-hour",
33188                 "meter-per-second": "foot-per-second",
33189                 "kilometer-per-second": "mile-per-second"
33190             },
33191             "imperial": {
33192                 "kilometer-per-hour": "mile-per-hour",
33193                 "meter-per-second": "foot-per-second",
33194                 "kilometer-per-second": "mile-per-second"
33195             }
33196         }
33197     }
33198 };
33199 
33200 /**
33201  * Return the type of this measurement. Examples are "mass",
33202  * "length", "speed", etc. Measurements can only be converted
33203  * to measurements of the same type.<p>
33204  *
33205  * The type of the units is determined automatically from the
33206  * units. For example, the unit "grams" is type "mass". Use the
33207  * static call {@link Measurement.getAvailableUnits}
33208  * to find out what units this version of ilib supports.
33209  *
33210  * @return {string} the name of the type of this measurement
33211  */
33212 VelocityUnit.prototype.getMeasure = function() {
33213     return "velocity";
33214 };
33215 
33216 VelocityUnit.aliases = {
33217     "foot/sec": "foot-per-second",
33218     "foot/s": "foot-per-second",
33219     "feet/s": "foot-per-second",
33220     "f/s": "foot-per-second",
33221     "feet/second": "foot-per-second",
33222     "feet/sec": "foot-per-second",
33223     "meter/sec": "meter-per-second",
33224     "meter/s": "meter-per-second",
33225     "meters/s": "meter-per-second",
33226     "metre/sec": "meter-per-second",
33227     "metre/s": "meter-per-second",
33228     "metres/s": "meter-per-second",
33229     "mt/sec": "meter-per-second",
33230     "m/sec": "meter-per-second",
33231     "mt/s": "meter-per-second",
33232     "m/s": "meter-per-second",
33233     "mps": "meter-per-second",
33234     "meters/second": "meter-per-second",
33235     "meters/sec": "meter-per-second",
33236     "kilometer/hour": "kilometer-per-hour",
33237     "km/hour": "kilometer-per-hour",
33238     "kilometers/hour": "kilometer-per-hour",
33239     "kilometer per hour": "kilometer-per-hour",
33240     "kilometers per hour": "kilometer-per-hour",
33241     "kph": "kilometer-per-hour",
33242     "kmh": "kilometer-per-hour",
33243     "km/h": "kilometer-per-hour",
33244     "kilometer/h": "kilometer-per-hour",
33245     "kilometers/h": "kilometer-per-hour",
33246     "km/hr": "kilometer-per-hour",
33247     "kilometer/hr": "kilometer-per-hour",
33248     "kilometers/hr": "kilometer-per-hour",
33249     "kilometre/hour": "kilometer-per-hour",
33250     "mph": "mile-per-hour",
33251     "mile/hour": "mile-per-hour",
33252     "mile per hour": "mile-per-hour",
33253     "miles per hour": "miles-per-hour",
33254     "mile/hr": "mile-per-hour",
33255     "mile/h": "mile-per-hour",
33256     "miles/h": "mile-per-hour",
33257     "miles/hr": "mile-per-hour",
33258     "miles/hour": "mile-per-hour",
33259     "kn": "knot",
33260     "kt": "knot",
33261     "kts": "knot",
33262     "knots": "knot",
33263     "nm/h": "knot",
33264     "nm/hr": "knot",
33265     "nauticalmile/h": "knot",
33266     "nauticalmile/hr": "knot",
33267     "nauticalmile/hour": "knot",
33268     "nauticalmiles/hr": "knot",
33269     "nauticalmiles/hour": "knot",
33270     "nautical mile per hour": "knot",
33271     "nautical miles per hour": "knot",
33272     "nautical-mile/h": "knot",
33273     "nautical-mile/hr": "knot",
33274     "nautical-mile/hour": "knot",
33275     "nautical-miles/hr": "knot",
33276     "nautical-miles/hour": "knot",
33277     "knot": "knot",
33278     "kilometer/second": "kilometer-per-second",
33279     "kilometer/sec": "kilometer-per-second",
33280     "kilometre/sec": "kilometer-per-second",
33281     "Kilometre/sec": "kilometer-per-second",
33282     "kilometers/second": "kilometer-per-second",
33283     "kilometers/sec": "kilometer-per-second",
33284     "kilometres/sec": "kilometer-per-second",
33285     "Kilometres/sec": "kilometer-per-second",
33286     "km/sec": "kilometer-per-second",
33287     "Km/s": "kilometer-per-second",
33288     "km/s": "kilometer-per-second",
33289     "miles/second": "mile-per-second",
33290     "miles/sec": "mile-per-second",
33291     "miles/s": "mile-per-second",
33292     "mile/s": "mile-per-second",
33293     "mile/sec": "mile-per-second",
33294     "Mile/s": "mile-per-second"
33295 };
33296 
33297 (function() {
33298     VelocityUnit.aliasesLower = {};
33299     for (var a in VelocityUnit.aliases) {
33300         VelocityUnit.aliasesLower[a.toLowerCase()] = VelocityUnit.aliases[a];
33301     }
33302 })();
33303 
33304 /**
33305  * Convert a speed to another measure.
33306  * @static
33307  * @param to {string} unit to convert to
33308  * @param from {string} unit to convert from
33309  * @param speed {number} amount to be convert
33310  * @returns {number|undefined} the converted amount
33311  */
33312 VelocityUnit.convert = function(to, from, speed) {
33313     from = Measurement.getUnitIdCaseInsensitive(VelocityUnit, from) || from;
33314     to = Measurement.getUnitIdCaseInsensitive(VelocityUnit, to) || to;
33315     var fromRow = VelocityUnit.ratios[from];
33316     var toRow = VelocityUnit.ratios[to];
33317     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
33318         return undefined;
33319     }
33320     var result = speed * fromRow[toRow[0]];
33321     return result;
33322 };
33323 
33324 /**
33325  * @private
33326  * @static
33327  */
33328 VelocityUnit.getMeasures = function () {
33329     return Object.keys(VelocityUnit.ratios);
33330 };
33331 
33332 //register with the factory method
33333 Measurement._constructors["speed"] = VelocityUnit;
33334 Measurement._constructors["velocity"] = VelocityUnit;
33335 
33336 
33337 /*< VolumeUnit.js */
33338 /*
33339  * VolumeUnit.js - Unit conversions for volume measurements
33340  *
33341  * Copyright © 2014-2015, 2018 JEDLSoft
33342  *
33343  * Licensed under the Apache License, Version 2.0 (the "License");
33344  * you may not use this file except in compliance with the License.
33345  * You may obtain a copy of the License at
33346  *
33347  *     http://www.apache.org/licenses/LICENSE-2.0
33348  *
33349  *
33350  * Unless required by applicable law or agreed to in writing, software
33351  * distributed under the License is distributed on an "AS IS" BASIS,
33352  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33353  *
33354  * See the License for the specific language governing permissions and
33355  * limitations under the License.
33356  */
33357 
33358 /*
33359 !depends
33360 Measurement.js
33361 */
33362 
33363 
33364 /**
33365  * @class
33366  * Create a new Volume measurement instance.
33367  *
33368  * @constructor
33369  * @extends Measurement
33370  * @param options {{unit:string,amount:number|string|undefined}} Options controlling
33371  * the construction of this instance
33372  */
33373 var VolumeUnit = function (options) {
33374     this.unit = "cubic-meter";
33375     this.amount = 0;
33376 
33377     this.ratios = VolumeUnit.ratios;
33378     this.aliases = VolumeUnit.aliases;
33379     this.aliasesLower = VolumeUnit.aliasesLower;
33380     this.systems = VolumeUnit.systems;
33381 
33382     this.parent.call(this, options);
33383 };
33384 
33385 VolumeUnit.prototype = new Measurement();
33386 VolumeUnit.prototype.parent = Measurement;
33387 VolumeUnit.prototype.constructor = VolumeUnit;
33388 
33389 VolumeUnit.ratios = {
33390     /*                     index, tsp,      tbsp,               cubic inch  us ounce, cup,        pint,       quart,      gallon,      cubic foot,  milliliter  liter,      cubic meter, imperial tsp,  imperial tbsp,      imperial ounce,    imperial cup,   imperial pint,  imperial quart, imperial gal, cubic cm    */
33391     "teaspoon" :           [1,    1,        0.3333333333333333, 0.300781,   0.166667, 0.0208333,  0.0104167,  0.00520833, 0.00130208,  0.000174063, 4.92892,    0.00492892, 4.9289e-6,   0.832674,      0.277558,           0.173474,          0.0173474,          0.00867369,     0.00433684,     0.00108421,   4.92892    ],
33392     "tablespoon":          [2,    3,        1,                  0.902344,   0.5,      0.0625,     0.0312,     0.015625,   0.00390625,  0.00052219,  14.7868,    0.0147868,  1.4787e-5,   2.49802,       0.832674,           0.520421,          0.0520421,          0.0260211,      0.0130105,      0.00325263,   14.7868    ],
33393     "cubic-inch":          [3,    3.32468,  1.10823,            1,          0.554113, 0.0692641,  0.034632,   0.017316,   0.004329,    0.000578704, 16.3871,    0.0163871,  1.6387e-5,   2.76837,       0.92279,            0.576744,          0.057674402,        0.0288372,      0.0144186,      0.00360465,   16.3871    ],
33394     "fluid-ounce":         [4,    6,        2,                  1.80469,    1,        0.125,      0.0625,     0.03125,    0.0078125,   0.00104438,  29.5735,    0.0295735,  2.9574e-5,   4.99604,       1.04084,            1.04084,           0.10408427,         0.0520421,      0.0260211,      0.00650526,   29.5735    ],
33395     "cup":                 [5,    48,       16,                 14.4375,    8,        1,          0.5,        0.25,       0.0625,      0.00835503,  236.588,    0.236588,   0.000236588, 39.9683,       13.3228,            8.32674,           0.83267418,         0.416337,       0.208168,       0.0520421,    236.588    ],
33396     "pint":                [6,    96,       32,                 28.875,     16,       2,          1,          0.5,        0.125,       0.0167101,   473.176,    0.473176,   0.000473176, 79.9367,       26.6456,            16.6535,           1.66534836,         0.83267418,     0.416337,       0.104084,     473.176    ],
33397     "quart":               [7,    192,      64,                 57.75,      32,       4,          2,          1,          0.25,        0.0334201,   946.353,    0.946353,   0.000946353, 159.873,       53.2911,            33.307,            3.33069674,         1.66534836,     0.832674,       0.208168,     946.353    ],
33398     "gallon":              [8,    768,      256,                231,        128,      16,         8,          4,          1,           0.133681,    3785.41,    3.78541,    0.00378541,  639.494,       213.165,            133.228,           13.322787,          6.66139,        3.33069674,     0.832674,     3785.41    ],
33399     "cubic-foot":          [9,    5745.04,  1915.01,            1728,       957.506,  119.688,    59.8442,    29.9221,    7.48052,     1,           28316.8,    28.3168,    0.0283168,   4783.74,       1594.58,            996.613,           99.661367,          49.8307,        24.9153,        6.22883,      28316.8    ],
33400     "milliliter":          [10,   0.202884, 0.067628,           0.0610237,  0.033814, 0.00422675, 0.00211338, 0.00105669, 0.000264172, 3.5315e-5,   1,          0.001,      1e-6,        0.168936,      0.0563121,          0.0351951,         0.00351950797,      0.00175975,     0.000879877,    0.000219969,  1          ],
33401     "liter":               [11,   202.884,  67.628,             61.0237,    33.814,   4.22675,    2.11338,    1.05669,    0.264172,    0.0353147,   1000,       1,          0.001,       56.3121,       56.3121,            35.191,            3.51950797,         1.75975,        0.879877,       0.219969,     1000       ],
33402     "cubic-meter":         [12,   202884,   67628,              61023.7,    33814,    4226.75,    2113.38,    1056.69,    264.172,     35.3147,     1e+6,       1000,       1,           168936,        56312.1,            35195.1,           3519.50797,         1759.75,        879.877,        219.969,      1e+6       ],
33403     "teaspoon-imperial":   [13,   1.20095,  0.200158,           0.361223,   0.600475, 0.0250198,  0.0125099,  0.00625495, 0.00156374,  0.000209041, 5.91939,    0.00591939, 5.9194e-6,   1,             0.3333333333333333, 0.208333333333333, 0.0208333333333333, 0.0104167,      0.00520833,     0.00130208,   5.91939    ],
33404     "tablespoon-imperial": [14,   3.60285,  1.20095,            1.08367,    0.600475, 0.0750594,  0.0375297,  0.0187649,  0.00469121,  0.000627124, 17.7582,    0.0177582,  1.7758e-5,   3,             1,                  0.625,             0.0625,             0.03125,        0.015625,       0.00390625,   17.7582    ],
33405     "ounce-imperial":      [15,   5.76456,  1.92152,            1.73387,    0.96076,  0.120095,   0.0600475,  0.0300238,  0.00750594,  0.0010034,   28.4131,    0.0284131,  2.8413e-5,   4.8,           1.6,                1,                 0.1,                0.05,           0.025,          0.00625,      28.4131    ],
33406     "pint-imperial":       [17,   115.291,  38.4304,            34.6774,    19.2152,  2.4019,     1.20095,    0.600475,   0.150119,    0.020068,    568.261,    0.568261,   0.000568261, 96,            32,                 20,                2,                  1,              0.5,            0.125,        568.261    ],
33407     "cup-imperial":        [16,   57.64557, 19.2151988,         17.3387145, 9.6075994,1.20095,    0.60047496, 0.30023748, 0.07505937,  0.010033978, 284.130625, 0.28413063, 2.841306e-4, 48,            16,                 10,                1,                  0.5,            0.25,           0.0625,       284.130625 ],
33408     "quart-imperial":      [18,   230.582,  76.8608,            69.3549,    38.4304,  4.8038,     2.4019,     1.20095,    0.300238,    0.0401359,   1136.52,    1.13652,    0.00113652,  192,           64,                 40,                4,                  2,              1,              0.25,         1136.52    ],
33409     "gallon-imperial":     [19,   922.33,   307.443,            277.42,     153.722,  19.2152,    9.6076,     4.8038,     1.20095,     0.160544,    4546.09,    4.54609,    0.00454609,  768,           256,                160,               16,                 8,              4,              1,            4546.09    ],
33410     "cubic-centimeter":    [20,   0.202884, 0.067628,           0.0610237,  0.033814, 0.00422675, 0.00211338, 0.00105669, 0.000264172, 3.5315e-5,   1,          0.001,      1e-6,        0.168936,      0.0563121,          0.0351951,         0.00351950797,      0.00175975,     0.000879877,    0.000219969,  1          ]
33411 };
33412 
33413 /**
33414  * Return the type of this measurement. Examples are "mass",
33415  * "length", "speed", etc. Measurements can only be converted
33416  * to measurements of the same type.<p>
33417  *
33418  * The type of the units is determined automatically from the
33419  * units. For example, the unit "grams" is type "mass". Use the
33420  * static call {@link Measurement.getAvailableUnits}
33421  * to find out what units this version of ilib supports.
33422  *
33423  * @return {string} the name of the type of this measurement
33424  */
33425 VolumeUnit.prototype.getMeasure = function() {
33426     return "volume";
33427 };
33428 
33429 VolumeUnit.aliases = {
33430     "US gal": "gallon",
33431     "US gallon": "gallon",
33432     "US Gal": "gallon",
33433     "US Gallons": "gallon",
33434     "Gal(US)": "gallon",
33435     "gal(US)": "gallon",
33436     "gallon": "gallon",
33437     "gallons": "gallon",
33438     "quart": "quart",
33439     "quarts": "quart",
33440     "US quart": "quart",
33441     "US quarts": "quart",
33442     "US Quart": "quart",
33443     "US Quarts": "quart",
33444     "US qt": "quart",
33445     "Qt(US)": "quart",
33446     "qt(US)": "quart",
33447     "US pint": "pint",
33448     "US Pint": "pint",
33449     "pint": "pint",
33450     "pint(US)": "pint",
33451     "Pint(US)": "pint",
33452     "US pints": "pint",
33453     "US Pints": "pint",
33454     "pints": "pint",
33455     "pints(US)": "pint",
33456     "Pints(US)": "pint",
33457     "US cup": "cup",
33458     "US Cup": "cup",
33459     "cup(US)": "cup",
33460     "Cup(US)": "cup",
33461     "cup": "cup",
33462     "US cups": "cup",
33463     "US Cups": "cup",
33464     "cups(US)": "cup",
33465     "Cups(US)": "cup",
33466     "cups": "cup",
33467     "us ounce": "fluid-ounce",
33468     "US ounce": "fluid-ounce",
33469     "us ounces": "fluid-ounce",
33470     "US ounces": "fluid-ounce",
33471     "fluid ounce": "fluid-ounce",
33472     "fluid ounces": "fluid-ounce",
33473     "Fluid Ounce": "fluid-ounce",
33474     "Fluid Ounces": "fluid-ounce",
33475     "℥": "fluid-ounce",
33476     "US Oz": "fluid-ounce",
33477     "oz(US)": "fluid-ounce",
33478     "Oz(US)": "fluid-ounce",
33479     "US tbsp": "tablespoon",
33480     "tbsp": "tablespoon",
33481     "tbsp(US)": "tablespoon",
33482     "US tablespoon": "tablespoon",
33483     "US tsp": "teaspoon",
33484     "US teaspoon": "teaspoon",
33485     "tsp(US)": "teaspoon",
33486     "tsp": "teaspoon",
33487     "Cubic meter": "cubic-meter",
33488     "cubic meter": "cubic-meter",
33489     "Cubic metre": "cubic-meter",
33490     "cubic metre": "cubic-meter",
33491     "cu meter": "cubic-meter",
33492     "cu metre": "cubic-meter",
33493     "Cubic meters": "cubic-meter",
33494     "cubic meters": "cubic-meter",
33495     "Cubic metres": "cubic-meter",
33496     "cubic metres": "cubic-meter",
33497     "cu meters": "cubic-meter",
33498     "cu metres": "cubic-meter",
33499     "cu m": "cubic-meter",
33500     "m3": "cubic-meter",
33501     "m³": "cubic-meter",
33502     "Cubic Centimeter": "cubic-centimeter",
33503     "cubic centimeter": "cubic-centimeter",
33504     "Cubic Centimetre": "cubic-centimeter",
33505     "cubic centimetre": "cubic-centimeter",
33506     "cu centimeter": "cubic-centimeter",
33507     "cu centimetre": "cubic-centimeter",
33508     "Cubic Centimeters": "cubic-centimeter",
33509     "cubic centimeters": "cubic-centimeter",
33510     "Cubic Centimetres": "cubic-centimeter",
33511     "cubic centimetres": "cubic-centimeter",
33512     "cu centimeters": "cubic-centimeter",
33513     "cu centimetres": "cubic-centimeter",
33514     "cu cm": "cubic-centimeter",
33515     "cm3": "cubic-centimeter",
33516     "cm³": "cubic-centimeter",
33517     "cc": "cubic-centimeter",
33518     "Liter": "liter",
33519     "Liters": "liter",
33520     "liter": "liter",
33521     "liters": "liter",
33522     "L": "liter",
33523     "l": "liter",
33524     "Milliliter": "milliliter",
33525     "ML": "milliliter",
33526     "ml": "milliliter",
33527     "milliliter": "milliliter",
33528     "milliliters": "milliliter",
33529     "mL": "milliliter",
33530     "Imperial gal": "gallon-imperial",
33531     "imperial gallon": "gallon-imperial",
33532     "Imperial gallon": "gallon-imperial",
33533     "imperial gallons": "gallon-imperial",
33534     "Imperial gallons": "gallon-imperial",
33535     "gallon(imperial)": "gallon-imperial",
33536     "gallon(imp)": "gallon-imperial",
33537     "gallons(imperial)": "gallon-imperial",
33538     "gallons(imp)": "gallon-imperial",
33539     "gal(imperial)": "gallon-imperial",
33540     "gal(imp)": "gallon-imperial",
33541     "gallon (imperial)": "gallon-imperial",
33542     "gallon (imp)": "gallon-imperial",
33543     "gallons (imperial)": "gallon-imperial",
33544     "gallons (imp)": "gallon-imperial",
33545     "gal (imperial)": "gallon-imperial",
33546     "gal (imp)": "gallon-imperial",
33547     "Imperial quart": "quart-imperial",
33548     "imperial quart": "quart-imperial",
33549     "Imperial Quart": "quart-imperial",
33550     "Imperial quarts": "quart-imperial",
33551     "imperial quarts": "quart-imperial",
33552     "Imperial Quarts": "quart-imperial",
33553     "Imperial qt": "quart-imperial",
33554     "qt(Imperial)": "quart-imperial",
33555     "qt(Imp)": "quart-imperial",
33556     "qt (Imperial)": "quart-imperial",
33557     "qt (Imp)": "quart-imperial",
33558     "quart(imperial)": "quart-imperial",
33559     "quart(imp)": "quart-imperial",
33560     "quart (imperial)": "quart-imperial",
33561     "quart (imp)": "quart-imperial",
33562     "quarts(imperial)": "quart-imperial",
33563     "quarts(imp)": "quart-imperial",
33564     "quarts (imperial)": "quart-imperial",
33565     "quarts (imp)": "quart-imperial",
33566     "Imperial pint": "pint-imperial",
33567     "imperial pint": "pint-imperial",
33568     "Imperial pints": "pint-imperial",
33569     "imperial pints": "pint-imperial",
33570     "pint(Imperial)": "pint-imperial",
33571     "pints(Imperial)": "pint-imperial",
33572     "pint(Imp)": "pint-imperial",
33573     "pints(Imp)": "pint-imperial",
33574     "pint (Imperial)": "pint-imperial",
33575     "pints (Imperial)": "pint-imperial",
33576     "pint (Imp)": "pint-imperial",
33577     "pints (Imp)": "pint-imperial",
33578     "imperial cup": "cup-imperial",
33579     "Imperial Cup": "cup-imperial",
33580     "cup(imperial)": "cup-imperial",
33581     "Cup(Imperial)": "cup-imperial",
33582     "cup (imperial)": "cup-imperial",
33583     "Cup (Imperial)": "cup-imperial",
33584     "cup(imp)": "cup-imperial",
33585     "Cup(Imp)": "cup-imperial",
33586     "cup (imp)": "cup-imperial",
33587     "Cup (Imp)": "cup-imperial",
33588     "imperial cups": "cup-imperial",
33589     "Imperial Cups": "cup-imperial",
33590     "cups(imperial)": "cup-imperial",
33591     "Cups(Imperial)": "cup-imperial",
33592     "cups (imperial)": "cup-imperial",
33593     "Cups (Imperial)": "cup-imperial",
33594     "cups(imp)": "cup-imperial",
33595     "Cups(Imp)": "cup-imperial",
33596     "cups (imp)": "cup-imperial",
33597     "Cups (Imp)": "cup-imperial",
33598     "imperial oz": "ounce-imperial",
33599     "imperial ounce": "ounce-imperial",
33600     "Imperial Ounce": "ounce-imperial",
33601     "imperial ounces": "ounce-imperial",
33602     "Imperial Ounces": "ounce-imperial",
33603     "Imperial tablespoon": "tablespoon-imperial",
33604     "imperial tablespoon": "tablespoon-imperial",
33605     "tablespoon(Imperial)": "tablespoon-imperial",
33606     "tablespoon(Imp)": "tablespoon-imperial",
33607     "tablespoon (Imperial)": "tablespoon-imperial",
33608     "tablespoon (Imp)": "tablespoon-imperial",
33609     "Imperial tablespoons": "tablespoon-imperial",
33610     "imperial tablespoons": "tablespoon-imperial",
33611     "tablespoons(Imperial)": "tablespoon-imperial",
33612     "tablespoons(Imp)": "tablespoon-imperial",
33613     "tablespoons (Imperial)": "tablespoon-imperial",
33614     "tablespoons (Imp)": "tablespoon-imperial",
33615     "Imperial tbsp": "tablespoon-imperial",
33616     "imperial tbsp": "tablespoon-imperial",
33617     "tbsp(Imperial)": "tablespoon-imperial",
33618     "tbsp(Imp)": "tablespoon-imperial",
33619     "tbsp (Imperial)": "tablespoon-imperial",
33620     "tbsp (Imp)": "tablespoon-imperial",
33621     "Imperial teaspoon": "teaspoon-imperial",
33622     "imperial teaspoon": "teaspoon-imperial",
33623     "Imperial teaspoons": "teaspoon-imperial",
33624     "imperial teaspoons": "teaspoon-imperial",
33625     "Imperial tsp": "teaspoon-imperial",
33626     "imperial tsp": "teaspoon-imperial",
33627     "teaspoon(Imperial)": "teaspoon-imperial",
33628     "teaspoon(Imp)": "teaspoon-imperial",
33629     "teaspoons(Imperial)": "teaspoon-imperial",
33630     "teaspoons(Imp)": "teaspoon-imperial",
33631     "tsp(Imperial)": "teaspoon-imperial",
33632     "tsp(Imp)": "teaspoon-imperial",
33633     "teaspoon (Imperial)": "teaspoon-imperial",
33634     "teaspoon (Imp)": "teaspoon-imperial",
33635     "teaspoons (Imperial)": "teaspoon-imperial",
33636     "teaspoons (Imp)": "teaspoon-imperial",
33637     "tsp (Imperial)": "teaspoon-imperial",
33638     "tsp (Imp)": "teaspoon-imperial",
33639     "Cubic foot": "cubic-foot",
33640     "cubic foot": "cubic-foot",
33641     "Cubic Foot": "cubic-foot",
33642     "Cubic feet": "cubic-foot",
33643     "cubic Feet": "cubic-foot",
33644     "cubic ft": "cubic-foot",
33645     "ft3": "cubic-foot",
33646     "Cubic inch": "cubic-inch",
33647     "Cubic inches": "cubic-inch",
33648     "cubic inches": "cubic-inch",
33649     "cubic inch": "cubic-inch",
33650     "cubic in": "cubic-inch",
33651     "cu in": "cubic-inch",
33652     "cu inch": "cubic-inch",
33653     "inch³": "cubic-inch",
33654     "in³": "cubic-inch",
33655     "inch^3": "cubic-inch",
33656     "in^3": "cubic-inch",
33657     "c.i": "cubic-inch",
33658     "CI": "cubic-inch",
33659     "cui": "cubic-inch"
33660 };
33661 
33662 (function() {
33663     VolumeUnit.aliasesLower = {};
33664     for (var a in VolumeUnit.aliases) {
33665         VolumeUnit.aliasesLower[a.toLowerCase()] = VolumeUnit.aliases[a];
33666     }
33667 })();
33668 
33669 
33670 /**
33671  * Convert a volume to another measure.
33672  * @static
33673  * @param to {string} unit to convert to
33674  * @param from {string} unit to convert from
33675  * @param volume {number} amount to be convert
33676  * @returns {number|undefined} the converted amount
33677  */
33678 VolumeUnit.convert = function(to, from, volume) {
33679     from = Measurement.getUnitIdCaseInsensitive(VolumeUnit, from) || from;
33680     to = Measurement.getUnitIdCaseInsensitive(VolumeUnit, to) || to;
33681     var fromRow = VolumeUnit.ratios[from];
33682     var toRow = VolumeUnit.ratios[to];
33683     if (typeof(from) === 'undefined' || typeof(to) === 'undefined') {
33684         return undefined;
33685     }
33686     var result = volume * fromRow[toRow[0]];
33687     return result;
33688 };
33689 
33690 /**
33691  * Return a new instance of this type of measurement.
33692  *
33693  * @param {Object} params parameters to the constructor
33694  * @return {Measurement} a measurement subclass instance
33695  */
33696 VolumeUnit.prototype.newUnit = function(params) {
33697     return new VolumeUnit(params);
33698 };
33699 
33700 /**
33701  * @private
33702  * @static
33703  */
33704 VolumeUnit.getMeasures = function () {
33705     return Object.keys(VolumeUnit.ratios);
33706 };
33707 
33708 VolumeUnit.systems = {
33709     "metric": [
33710         "milliliter",
33711         "liter",
33712         "cubic-meter"
33713     ],
33714     "uscustomary": [
33715         "teaspoon",
33716         "tablespoon",
33717         "cubic-inch",
33718         "fluid-ounce",
33719         "cup",
33720         "pint",
33721         "quart",
33722         "gallon",
33723         "cubic-foot"
33724     ],
33725     "imperial": [
33726         "teaspoon-imperial",
33727         "tablespoon-imperial",
33728         "ounce-imperial",
33729         "cup-imperial",
33730         "pint-imperial",
33731         "quart-imperial",
33732         "gallon-imperial"
33733     ],
33734     "conversions": {
33735         "metric": {
33736             "uscustomary": {
33737                 "milliliter": "teaspoon",
33738                 "cubic-centimeter": "teaspoon",
33739                 "liter": "quart",
33740                 "cubic-meter": "cubic-foot"
33741             },
33742             "imperial": {
33743                 "milliliter": "teaspoon-imperial",
33744                 "cubic-centimeter": "teaspoon-imperial",
33745                 "liter": "quart-imperial",
33746                 "cubic-meter": "gallon-imperial"
33747             }
33748         },
33749         "imperial": {
33750             "metric": {
33751                 "teaspoon-imperial": "milliliter",
33752                 "tablespoon-imperial": "milliliter",
33753                 "ounce-imperial": "milliliter",
33754                 "pint-imperial": "liter",
33755                 "quart-imperial": "liter",
33756                 "gallon-imperial": "cubic-meter"
33757             },
33758             "uscustomary": {
33759                 "teaspoon-imperial": "teaspoon",
33760                 "tablespoon-imperial": "tablespoon",
33761                 "ounce-imperial": "fluid-ounce",
33762                 "pint-imperial": "pint",
33763                 "quart-imperial": "quart",
33764                 "gallon-imperial": "gallon"
33765             }
33766         },
33767         "uscustomary": {
33768             "imperial": {
33769                 "teaspoon": "teaspoon-imperial",
33770                 "tablespoon": "tablespoon-imperial",
33771                 "cubic-inch": "tablespoon-imperial",
33772                 "fluid-ounce": "ounce-imperial",
33773                 "cup": "ounce-imperial",
33774                 "pint": "pint-imperial",
33775                 "quart": "quart-imperial",
33776                 "gallon": "gallon-imperial",
33777                 "cubic-foot": "gallon-imperial"
33778             },
33779             "metric": {
33780                 "teaspoon": "milliliter",
33781                 "tablespoon": "milliliter",
33782                 "cubic-inch": "milliliter",
33783                 "fluid-ounce": "milliliter",
33784                 "cup": "milliliter",
33785                 "pint": "liter",
33786                 "quart": "liter",
33787                 "gallon": "cubic-meter",
33788                 "cubic-foot": "cubic-meter"
33789             }
33790         }
33791     }
33792 };
33793 
33794 //register with the factory method
33795 Measurement._constructors["volume"] = VolumeUnit;
33796 
33797 
33798 
33799 /*< MeasurementFactory.js */
33800 /*
33801  * MeasurementFactory.js - Function to instantiate the appropriate subclasses of
33802  * the Measurement class.
33803  *
33804  * Copyright © 2015, JEDLSoft
33805  *
33806  * Licensed under the Apache License, Version 2.0 (the "License");
33807  * you may not use this file except in compliance with the License.
33808  * You may obtain a copy of the License at
33809  *
33810  *     http://www.apache.org/licenses/LICENSE-2.0
33811  *
33812  * Unless required by applicable law or agreed to in writing, software
33813  * distributed under the License is distributed on an "AS IS" BASIS,
33814  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33815  *
33816  * See the License for the specific language governing permissions and
33817  * limitations under the License.
33818  */
33819 
33820 /*
33821 !depends
33822 UnknownUnit.js
33823 AreaUnit.js
33824 DigitalStorageUnit.js
33825 DigitalSpeedUnit.js
33826 EnergyUnit.js
33827 FuelConsumptionUnit.js
33828 LengthUnit.js
33829 MassUnit.js
33830 TemperatureUnit.js
33831 TimeUnit.js
33832 VelocityUnit.js
33833 VolumeUnit.js
33834 Measurement.js
33835 */
33836 
33837 // TODO: make these dependencies dynamic or at least generate them in the build
33838 // These will each add themselves to Measurement._constructors[]
33839 
33840 
33841 /**
33842  * Create a measurement subclass instance based on a particular measure
33843  * required. The measurement is immutable once
33844  * it is created, but it can be converted to other measurements later.<p>
33845  *
33846  * The options may contain any of the following properties:
33847  *
33848  * <ul>
33849  * <li><i>amount</i> - either a numeric amount for this measurement given
33850  * as a number of the specified units, or another Measurement instance
33851  * to convert to the requested units. If converting to new units, the type
33852  * of measure between the other instance's units and the current units
33853  * must be the same. That is, you can only convert one unit of mass to
33854  * another. You cannot convert a unit of mass into a unit of length.
33855  *
33856  * <li><i>unit</i> - units of this measurement. Use the
33857  * static call {@link MeasurementFactory.getAvailableUnits}
33858  * to find out what units this version of ilib supports. If the given unit
33859  * is not a base unit, the amount will be normalized to the number of base units
33860  * and stored as that number of base units.
33861  * For example, if an instance is constructed with 1 kg, this will be converted
33862  * automatically into 1000 g, as grams are the base unit and kg is merely a
33863  * commonly used scale of grams. 
33864  * </ul>
33865  *
33866  * Here are some examples of converting a length into new units. 
33867  * The first method is via this factory function by passing the old measurement 
33868  * in as the "amount" property.<p>
33869  * 
33870  * <pre>
33871  * var measurement1 = MeasurementFactory({
33872  *   amount: 5,
33873  *   units: "kilometers"
33874  * });
33875  * var measurement2 = MeasurementFactory({
33876  *   amount: measurement1,
33877  *   units: "miles"
33878  * });
33879  * </pre>
33880  * 
33881  * The value in measurement2 will end up being about 3.125 miles.<p>
33882  * 
33883  * The second method uses the convert method.<p>
33884  * 
33885  * <pre>
33886  * var measurement1 = MeasurementFactory({
33887  *   amount: 5,
33888  *   units: "kilometers"
33889  * });
33890  * var measurement2 = measurement1.convert("miles");
33891  * });
33892  * </pre>
33893  *
33894  * The value in measurement2 will again end up being about 3.125 miles.
33895  * 
33896  * @static
33897  * @param {Object=} options options that control the construction of this instance
33898  */
33899 var MeasurementFactory = function(options) {
33900     if (!options || typeof(options.unit) === 'undefined') {
33901         return undefined;
33902     }
33903 
33904     var measurement, measure = undefined;
33905 
33906     // first try in the existing case
33907     for (var c in Measurement._constructors) {
33908         measurement = Measurement._constructors[c];
33909         if (Measurement.getUnitId(measurement, options.unit)) {
33910             measure = c;
33911             break;
33912         }
33913     }
33914 
33915     if (!measure) {
33916         // if it wasn't found before, try again in lower case -- this may recognize incorrectly because some
33917         // units can differ only in their case like "mm" and "Mm"
33918         for (var c in Measurement._constructors) {
33919             measurement = Measurement._constructors[c];
33920             if (typeof(Measurement.getUnitIdCaseInsensitive(measurement, options.unit)) !== 'undefined') {
33921                 measure = c;
33922                 break;
33923             }
33924         }
33925     }
33926 
33927     if (!measure || typeof(measure) === 'undefined') {
33928         return new UnknownUnit({
33929             unit: options.unit,
33930             amount: options.amount
33931         });
33932     } else {
33933         return new Measurement._constructors[measure](options);
33934     }
33935 };
33936 
33937 /**
33938  * Return a list of all possible units that this version of ilib supports.
33939  * Typically, the units are given as their full names in English. Unit names
33940  * are case-insensitive.
33941  *
33942  * @static
33943  * @return {Array.<string>} an array of strings containing names of measurement 
33944  * units available
33945  */
33946 MeasurementFactory.getAvailableUnits = function () {
33947 	var units = [];
33948 	for (var c in Measurement._constructors) {
33949 		var measure = Measurement._constructors[c];
33950 		units = units.concat(measure.getMeasures());
33951 	}
33952 	return units;
33953 };
33954 
33955 
33956 
33957 /*< ListFmt.js */
33958 /*
33959  * ListFmt.js - Represent a list formatter.
33960  * 
33961  * Copyright © 2017, JEDLSoft
33962  *
33963  * Licensed under the Apache License, Version 2.0 (the "License");
33964  * you may not use this file except in compliance with the License.
33965  * You may obtain a copy of the License at
33966  *
33967  *     http://www.apache.org/licenses/LICENSE-2.0
33968  *
33969  * Unless required by applicable law or agreed to in writing, software
33970  * distributed under the License is distributed on an "AS IS" BASIS,
33971  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
33972  *
33973  * See the License for the specific language governing permissions and
33974  * limitations under the License.
33975  */
33976 
33977 /*
33978 !depends 
33979 ilib.js 
33980 Utils.js
33981 Locale.js 
33982 */
33983 
33984 // !data list
33985 
33986 
33987 
33988 /**
33989  * @class
33990  * Create a new list formatter object that formats lists of items according to 
33991  * the options.<p>
33992  * 
33993  * The options object can contain zero or more of the following parameters:
33994  *
33995  * <ul>
33996  * <li><i>locale</i> locale to use to format this list, or undefined to use the 
33997  * default locale
33998  * 
33999  * <li><i>length</i> - Specify the length of the format to use. The length is the approximate size of the 
34000  * formatted string.
34001  * 
34002  * <ul>
34003  * <li><i>short</i> 
34004  * <li><i>medium</i>  
34005  * <li><i>long</i>  
34006  * <li><i>full</i>
34007  * </ul>
34008  *
34009  * <li><i>style</i> the name of style to use to format the list, or undefined 
34010  * to use the default "standard" style. another style option is "units".
34011  *
34012  * <li><i>onLoad</i> - a callback function to call when the locale data is fully loaded and the address has been 
34013  * parsed. When the onLoad option is given, the address formatter object 
34014  * will attempt to load any missing locale data using the ilib loader callback.
34015  * When the constructor is done (even if the data is already preassembled), the 
34016  * onLoad function is called with the current instance as a parameter, so this
34017  * callback can be used with preassembled or dynamic loading or a mix of the two. 
34018  * 
34019  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or 
34020  * asynchronously. If this option is given as "false", then the "onLoad"
34021  * callback must be given, as the instance returned from this constructor will
34022  * not be usable for a while. 
34023  *
34024  * <li><i>loadParams</i> - an object containing parameters to pass to the 
34025  * loader callback function when locale data is missing. The parameters are not
34026  * interpretted or modified in any way. They are simply passed along. The object 
34027  * may contain any property/value pairs as long as the calling code is in
34028  * agreement with the loader callback function as to what those parameters mean.
34029  * </ul>
34030  *
34031  * @constructor
34032  * @param {Object} options properties that control how this formatter behaves
34033  */
34034 var ListFmt = function(options) {
34035 	this.locale = new Locale();
34036 	this.sync = true;
34037 	this.style = "standard";
34038 	this.length = "short";
34039 	this.loadParams = {};
34040 	
34041 	if (options) {
34042 		if (options.locale) {
34043 			this.locale = options.locale;
34044 		}
34045 
34046 		if (typeof(options.sync) !== 'undefined') {
34047 			this.sync = !!options.sync;
34048 		}
34049 
34050 		if (options.length) {
34051 			this.length = options.length;
34052 		}
34053 
34054 		if (options.loadParams) {
34055 			this.loadParams = options.loadParams;
34056 		}
34057 
34058 		if (options.style) {
34059 			this.style = options.style;
34060 		}
34061 	}
34062 
34063 	Utils.loadData({
34064 		name: "list.json",
34065 		object: ListFmt,
34066 		locale: this.locale, 
34067 		sync: this.sync,
34068 		loadParams: this.loadParams,
34069 		callback: ilib.bind(this, function (fmtdata) {
34070 			this.fmtdata = fmtdata;
34071 			
34072 			if (options && typeof(options.onLoad) === 'function') {
34073 				options.onLoad(this);
34074 			}
34075 		})
34076 	});
34077 };
34078 
34079 /**
34080  * Format a list of strings as grammatical text that is appropriate 
34081  * for the locale of this formatter.
34082  * 
34083  * @param {Array.<string>} items an array of strings to format in
34084  * order that you would like them to appear
34085  * @returns {string} a string containing the list of items that
34086  * is grammatically correct for the locale of this formatter
34087  */
34088 
34089 ListFmt.prototype.format = function(items) {
34090 	if (!items || (!ilib.isArray(items))) {
34091 		return "";
34092 	}
34093 
34094 	var itemCount = items.length;
34095 	var fmtTemplate, formattedList;
34096 	var startFmt, middleFmt, endFmt;
34097 	var i;
34098 	
34099 	fmtTemplate = this.fmtdata[this.style][this.length] || this.fmtdata[this.style];
34100 	startFmt = fmtTemplate["start"];
34101 	middleFmt = fmtTemplate["middle"];
34102 	endFmt = fmtTemplate["end"];
34103 
34104 	if (itemCount === 0) {
34105 		return "";
34106 	}
34107 	else if (itemCount === 1) {
34108 		formattedList =  items.toString();
34109 
34110 	} else if ( itemCount === 2) {
34111 		fmtTemplate = fmtTemplate["2"];
34112 		formattedList = fmtTemplate.replace("{0}", items[0]).replace("{1}", items[1]);
34113 
34114 	} else {
34115 		for(i = itemCount; i >= 0 ; i--){
34116 			if (i == itemCount) {
34117 				formattedList = endFmt.replace("{0}", items[itemCount-2]).replace("{1}", items[itemCount-1]);
34118 				i = i-2;
34119 			} else if (i == 0) {
34120 				formattedList = startFmt.replace("{0}",items[i]).replace("{1}", formattedList);
34121 			}
34122 			 else {
34123 				formattedList = middleFmt.replace("{0}",items[i]).replace("{1}", formattedList);
34124 			}
34125 		}
34126 	}
34127 	return formattedList;
34128 };
34129 
34130 /**
34131  * Return the locale of this formatter.
34132  * 
34133  * @returns {string} the locale of this formatter
34134  */
34135 ListFmt.prototype.getLocale = function() {
34136 	return this.locale.getSpec();
34137 };
34138 
34139 /**
34140  * Return the style of names returned by this formatter
34141  * @return {string} the style of names returned by this formatter
34142  */
34143 ListFmt.prototype.getStyle = function() {
34144 	return this.style;
34145 };
34146 
34147 
34148 /*< UnitFmt.js */
34149 /*
34150  * UnitFmt.js - Unit formatter class
34151  *
34152  * Copyright © 2014-2015, 2018 JEDLSoft
34153  *
34154  * Licensed under the Apache License, Version 2.0 (the "License");
34155  * you may not use this file except in compliance with the License.
34156  * You may obtain a copy of the License at
34157  *
34158  *     http://www.apache.org/licenses/LICENSE-2.0
34159  *
34160  * Unless required by applicable law or agreed to in writing, software
34161  * distributed under the License is distributed on an "AS IS" BASIS,
34162  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34163  *
34164  * See the License for the specific language governing permissions and
34165  * limitations under the License.
34166  */
34167 
34168 /*
34169 !depends
34170 ilib.js
34171 Locale.js
34172 IString.js
34173 NumFmt.js
34174 Utils.js
34175 ListFmt.js
34176 Measurement.js
34177 */
34178 
34179 // !data unitfmt
34180 
34181 
34182 
34183 // for converting ilib lengths to the ones that are supported in cldr
34184 var lenMap = {
34185   "full": "long",
34186   "long": "long",
34187   "medium": "short",
34188   "short": "short"
34189 };
34190 
34191 /**
34192  * @class
34193  * Create a new unit formatter instance. The unit formatter is immutable once
34194  * it is created, but can format as many different strings with different values
34195  * as needed with the same options. Create different unit formatter instances
34196  * for different purposes and then keep them cached for use later if you have
34197  * more than one unit string to format.<p>
34198  *
34199  * The options may contain any of the following properties:
34200  *
34201  * <ul>
34202  * <li><i>locale</i> - locale to use when formatting the units. The locale also
34203  * controls the translation of the names of the units. If the locale is
34204  * not specified, then the default locale of the app or web page will be used.
34205  *
34206  * <li><i>autoScale</i> - when true, automatically scale the amount to get the smallest
34207  * number greater than 1, where possible, possibly by converting units within the locale's
34208  * measurement system. For example, if the current locale is "en-US", and we have
34209  * a measurement containing 278 fluid ounces, then the number "278" can be scaled down
34210  * by converting the units to a larger one such as gallons. The scaled size would be
34211  * 2.17188 gallons. Since iLib does not have a US customary measure larger than gallons,
34212  * it cannot scale it down any further. If the amount is less than the smallest measure
34213  * already, it cannot be scaled down any further and no autoscaling will be applied.
34214  * Default for the autoScale property is "true", so it only needs to be specified when
34215  * you want to turn off autoscaling.
34216  *
34217  * <li><i>autoConvert</i> - automatically convert the units to the nearest appropriate
34218  * measure of the same type in the measurement system used by the locale. For example,
34219  * if a measurement of length is given in meters, but the current locale is "en-US"
34220  * which uses the US Customary system, then the nearest appropriate measure would be
34221  * "yards", and the amount would be converted from meters to yards automatically before
34222  * being formatted. Default for the autoConvert property is "true", so it only needs to
34223  * be specified when you want to turn off autoconversion.
34224  *
34225  * <li><i>usage</i> - describe the reason for the measure. For example, the usage of
34226  * a formatter may be for a "person height", which implies that certain customary units
34227  * should be used, even though other measures in the same system may be more efficient.
34228  * In US Customary measures, a person's height is traditionally given in feet and inches,
34229  * even though yards, feet and inches would be more efficient and logical.<p>
34230  *
34231  * Specifying a usage implies that the
34232  * autoScale is turned on so that the measure can be scaled to the level required for
34233  * the customary measures for the usage. Setting the usage can also implicitly set
34234  * the style, the max- and minFractionDigits, roundingMode, length, etc. if those
34235  * options are not explicitly given in this options object. If they are given, the
34236  * explicit settings override the defaults of the usage.<p>
34237  *
34238  * Usages imply that the formatter should be used with a specific type of measurement.
34239  * If the format method is called on a measurement that is of the wrong type for the
34240  * usage, it will be formatted as a regular measurement with default options.<p>
34241  *
34242  * List of usages currently supported:
34243  *   <ul>
34244  *   <li><i>general</i> no specific usage with no preselected measures. (Default which does not
34245  *   restrict the units used for any type of measurement.)
34246  *   <li><i>floorSpace</i> area of the floor of a house or building
34247  *   <li><i>landArea</i> area of a piece of plot of land
34248  *   <li><i>networkingSpeed</i> speed of transfer of data over a network
34249  *   <li><i>audioSpeed</i> speed of transfer of audio data
34250  *   <li><i>interfaceSpeed</i> speed of transfer of data over a computer interface such as a USB or SATA bus
34251  *   <li><i>foodEnergy</i> amount of energy contains in food
34252  *   <li><i>electricalEnergy</i> amount of energy in electricity
34253  *   <li><i>heatingEnergy</i> amount of energy required to heat things such as water or home interiors
34254  *   <li><i>babyHeight</i> length of a baby
34255  *   <li><i>personHeight</i> height of an adult or child (not a baby)
34256  *   <li><i>vehicleDistance</i> distance traveled by a vehicle or aircraft (except a boat)
34257  *   <li><i>nauticalDistance</i> distance traveled by a boat
34258  *   <li><i>personWeight</i> weight/mass of an adult human or larger child
34259  *   <li><i>babyWeight</i> weight/mass of a baby or of small animals such as cats and dogs
34260  *   <li><i>vehicleWeight</i> weight/mass of a vehicle (including a boat)
34261  *   <li><i>drugWeight</i> weight/mass of a medicinal drug
34262  *   <li><i>vehicleSpeed</i> speed of travel of a vehicle or aircraft (except a boat)
34263  *   <li><i>nauticalSpeed</i> speed of travel of a boat
34264  *   <li><i>dryFoodVolume</i> volume of a dry food substance in a recipe such as flour
34265  *   <li><i>liquidFoodVolume</i> volume of a liquid food substance in a recipe such as milk
34266  *   <li><i>drinkVolume</i> volume of a drink
34267  *   <li><i>fuelVolume</i> volume of a vehicular fuel
34268  *   <li><i>engineVolume</i> volume of an engine's combustion space
34269  *   <li><i>storageVolume</i> volume of a mass storage tank
34270  *   <li><i>gasVolume</i> volume of a gas such as natural gas used in a home
34271  *   </ul>
34272  *
34273  * <li><i>style</i> - give the style of this formatter. This is used to
34274  * decide how to format the number and units when the number is not whole, or becomes
34275  * not whole after auto conversion and scaling. There are two basic styles
34276  * supported so far:
34277  *
34278  *   <ul>
34279  *   <li><i>numeric</i> - only the largest unit is used and the number is given as
34280  *   decimals. Example: "5.25 lbs"
34281  *   <li><i>list</i> - display the measure with a list of successively smaller-sized
34282  *   units. Example: "5 lbs 4 oz"
34283  *   </ul>
34284  *
34285  * The style is most useful for units which are not powers of 10 greater than the
34286  * smaller units as in the metric system, though it can be useful for metric measures
34287  * as well. Example: "2kg 381g".<p>
34288  *
34289  * The style may be set implicitly when you set the usage. For example, if the usage is
34290  * "personWeight", the style will be "numeric" and the maxFractionDigits will be 0. That
34291  * is, weight of adults and children are most often given in whole pounds. (eg. "172 lbs").
34292  * If the usage is "babyWeight", the style will be "list", and the measures will be pounds
34293  * and ounces. (eg. "7 lbs 2 oz").
34294  *
34295  * <li><i>length</i> - the length of the units text. This can be either "short" or "long"
34296  * with the default being "long". Example: a short units text might be "kph" and the
34297  * corresponding long units text would be "kilometers per hour". Typically, it is the
34298  * long units text that is translated per locale, though the short one may be as well.
34299  * Plurals are taken care of properly per locale as well.
34300  *
34301  * <li><i>maxFractionDigits</i> - the maximum number of digits that should appear in the
34302  * formatted output after the decimal. A value of -1 means unlimited, and 0 means only print
34303  * the integral part of the number.
34304  *
34305  * <li><i>minFractionDigits</i> - the minimum number of fractional digits that should
34306  * appear in the formatted output. If the number does not have enough fractional digits
34307  * to reach this minimum, the number will be zero-padded at the end to get to the limit.
34308  *
34309  * <li><i>significantDigits</i> - the number of significant digits that should appear
34310  * in the formatted output. If the given number is less than 1, this option will be ignored.
34311  *
34312  * <li><i>roundingMode</i> - When the maxFractionDigits or maxIntegerDigits is specified,
34313  * this property governs how the least significant digits are rounded to conform to that
34314  * maximum. The value of this property is a string with one of the following values:
34315  * <ul>
34316  *   <li><i>up</i> - round away from zero
34317  *   <li><i>down</i> - round towards zero. This has the effect of truncating the number
34318  *   <li><i>ceiling</i> - round towards positive infinity
34319  *   <li><i>floor</i> - round towards negative infinity
34320  *   <li><i>halfup</i> - round towards nearest neighbour. If equidistant, round up.
34321  *   <li><i>halfdown</i> - round towards nearest neighbour. If equidistant, round down.
34322  *   <li><i>halfeven</i> - round towards nearest neighbour. If equidistant, round towards the even neighbour
34323  *   <li><i>halfodd</i> - round towards nearest neighbour. If equidistant, round towards the odd neighbour
34324  * </ul>
34325  * Default if this is not specified is "halfup".
34326  *
34327  * <li><i>onLoad</i> - a callback function to call when the date format object is fully
34328  * loaded. When the onLoad option is given, the UnitFmt object will attempt to
34329  * load any missing locale data using the ilib loader callback.
34330  * When the constructor is done (even if the data is already preassembled), the
34331  * onLoad function is called with the current instance as a parameter, so this
34332  * callback can be used with preassembled or dynamic loading or a mix of the two.
34333  *
34334  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
34335  * asynchronously. If this option is given as "false", then the "onLoad"
34336  * callback must be given, as the instance returned from this constructor will
34337  * not be usable for a while.
34338  *
34339  * <li><i>loadParams</i> - an object containing parameters to pass to the
34340  * loader callback function when locale data is missing. The parameters are not
34341  * interpretted or modified in any way. They are simply passed along. The object
34342  * may contain any property/value pairs as long as the calling code is in
34343  * agreement with the loader callback function as to what those parameters mean.
34344  * </ul>
34345  *
34346  * Here is an example of how you might use the unit formatter to format a string with
34347  * the correct units.<p>
34348  *
34349  *
34350  * @constructor
34351  * @param {Object} options options governing the way this date formatter instance works
34352  */
34353 var UnitFmt = function(options) {
34354     var sync = true,
34355         loadParams = undefined;
34356 
34357     this.length = "long";
34358     this.scale  = true;
34359     this.measurementType = 'undefined';
34360     this.convert = true;
34361     this.locale = new Locale();
34362 
34363     options = options || {sync: true};
34364 
34365     if (options.locale) {
34366         this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
34367     }
34368 
34369     if (typeof(options.sync) === 'boolean') {
34370         sync = options.sync;
34371     }
34372 
34373     if (typeof(options.loadParams) !== 'undefined') {
34374         loadParams = options.loadParams;
34375     }
34376 
34377     if (options.length) {
34378         this.length = lenMap[options.length] || "long";
34379     }
34380 
34381     if (typeof(options.autoScale) === 'boolean') {
34382         this.scale = options.autoScale;
34383     }
34384 
34385     if (typeof(options.style) === 'string') {
34386         this.style = options.style;
34387     }
34388 
34389     if (typeof(options.usage) === 'string') {
34390         this.usage = options.usage;
34391     }
34392 
34393     if (typeof(options.autoConvert) === 'boolean') {
34394         this.convert = options.autoConvert;
34395     }
34396 
34397     if (typeof(options.useNative) === 'boolean') {
34398         this.useNative = options.useNative;
34399     }
34400 
34401     if (options.measurementSystem) {
34402         this.measurementSystem = options.measurementSystem;
34403     }
34404 
34405     if (typeof (options.maxFractionDigits) !== 'undefined') {
34406         /**
34407          * @private
34408          * @type {number|undefined}
34409          */
34410         this.maxFractionDigits = Number(options.maxFractionDigits);
34411     }
34412     if (typeof (options.minFractionDigits) !== 'undefined') {
34413         /**
34414          * @private
34415          * @type {number|undefined}
34416          */
34417         this.minFractionDigits = Number(options.minFractionDigits);
34418     }
34419 
34420     if (typeof (options.significantDigits) !== 'undefined') {
34421         /**
34422          * @private
34423          * @type {number|undefined}
34424          */
34425         this.significantDigits = Number(options.significantDigits);
34426     }
34427 
34428     /**
34429      * @private
34430      * @type {string}
34431      */
34432     this.roundingMode = options.roundingMode || "halfup";
34433 
34434     // ensure that the plural rules are loaded before we proceed
34435     IString.loadPlurals(sync, this.locale, loadParams, ilib.bind(this, function() {
34436         Utils.loadData({
34437             object: "UnitFmt",
34438             locale: this.locale,
34439             name: "unitfmt.json",
34440             sync: sync,
34441             loadParams: loadParams,
34442             callback: ilib.bind(this, function (format) {
34443                 this.template = format["unitfmt"][this.length];
34444 
34445                 if (this.usage && format.usages && format.usages[this.usage]) {
34446                     // if usage is not recognized, usageInfo will be undefined, which we will use to indicate unknown usage
34447                     this.usageInfo = format.usages[this.usage];
34448 
34449                     // default settings for this usage, but don't override the options that were passed in
34450                     if (typeof(this.maxFractionDigits) !== 'number' && typeof(this.usageInfo.maxFractionDigits) === 'number') {
34451                         this.maxFractionDigits = this.usageInfo.maxFractionDigits;
34452                     }
34453                     if (typeof(this.minFractionDigits) !== 'number' && typeof(this.usageInfo.minFractionDigits) === 'number') {
34454                         this.minFractionDigits = this.usageInfo.minFractionDigits;
34455                     }
34456                     if (typeof(this.significantDigits) !== 'number' && typeof(this.usageInfo.significantDigits) === 'number') {
34457                         this.significantDigits = this.usageInfo.significantDigits;
34458                     }
34459                     if (!this.measurementSystem && this.usageInfo.system) {
34460                         this.measurementSystem = this.usageInfo.system;
34461                     }
34462                     this.units = this.usageInfo.units;
34463                     if (!this.style && this.usageInfo.style) {
34464                         this.style = this.usageInfo.style;
34465                     }
34466 
34467                     if (this.usageInfo.systems) {
34468                         this.units = {
34469                             metric: this.usageInfo.systems.metric.units,
34470                             uscustomary: this.usageInfo.systems.uscustomary.units,
34471                             imperial: this.usageInfo.systems.imperial.units
34472                         };
34473                         this.numFmt = {};
34474                         this._initNumFmt(sync, loadParams, this.usageInfo.systems.metric, ilib.bind(this, function(numfmt) {
34475                             this.numFmt.metric = numfmt;
34476                             this._initNumFmt(sync, loadParams, this.usageInfo.systems.uscustomary, ilib.bind(this, function(numfmt) {
34477                                 this.numFmt.uscustomary = numfmt;
34478                                 this._initNumFmt(sync, loadParams, this.usageInfo.systems.imperial, ilib.bind(this, function(numfmt) {
34479                                     this.numFmt.imperial = numfmt;
34480                                     this._init(sync, loadParams, ilib.bind(this, function () {
34481                                         if (options && typeof(options.onLoad) === 'function') {
34482                                             options.onLoad(this);
34483                                         }
34484                                     }));
34485                                 }));
34486                             }));
34487                         }));
34488                     } else {
34489                         this._initFormatters(sync, loadParams, {}, ilib.bind(this, function() {
34490                             if (options && typeof(options.onLoad) === 'function') {
34491                                 options.onLoad(this);
34492                             }
34493                         }));
34494                     }
34495                 } else {
34496                     this._initFormatters(sync, loadParams, {}, ilib.bind(this, function() {
34497                         if (options && typeof(options.onLoad) === 'function') {
34498                             options.onLoad(this);
34499                         }
34500                     }));
34501                 }
34502             })
34503         });
34504     }));
34505 };
34506 
34507 UnitFmt.prototype = {
34508     /** @private */
34509     _initNumFmt: function(sync, loadParams, options, callback) {
34510         new NumFmt({
34511             locale: this.locale,
34512             useNative: this.useNative,
34513             maxFractionDigits: typeof(this.maxFractionDigits) !== 'undefined' ? this.maxFractionDigits : options.maxFractionDigits,
34514             minFractionDigits: typeof(this.minFractionDigits) !== 'undefined' ? this.minFractionDigits : options.minFractionDigits,
34515             significantDigits: typeof(this.significantDigits) !== 'undefined' ? this.significantDigits : options.significantDigits,
34516             roundingMode: this.roundingMode || options.roundingMode,
34517             sync: sync,
34518             loadParams: loadParams,
34519             onLoad: ilib.bind(this, function (numfmt) {
34520                 callback(numfmt);
34521             })
34522         });
34523     },
34524 
34525     _initFormatters: function(sync, loadParams, options, callback) {
34526         this._initNumFmt(sync, loadParams, {}, ilib.bind(this, function(numfmt) {
34527             this.numFmt = {
34528                 metric: numfmt,
34529                 uscustomary: numfmt,
34530                 imperial: numfmt
34531             };
34532 
34533             this._init(sync, loadParams, callback);
34534         }));
34535     },
34536 
34537     /** @private */
34538     _init: function(sync, loadParams, callback) {
34539         if (this.style === "list" || (this.usageInfo && this.usageInfo.systems &&
34540                 (this.usageInfo.systems.metric.style === "list" ||
34541                 this.usageInfo.systems.uscustomary.style === "list" ||
34542                 this.usageInfo.systems.imperial.style === "list"))) {
34543             new ListFmt({
34544                 locale: this.locale,
34545                 style: "unit",
34546                 sync: sync,
34547                 loadParams: loadParams,
34548                 onLoad: ilib.bind(this, function (listFmt) {
34549                     this.listFmt = listFmt;
34550                     callback();
34551                 })
34552             });
34553         } else {
34554             callback();
34555         }
34556     },
34557 
34558     /**
34559      * Return the locale used with this formatter instance.
34560      * @return {Locale} the Locale instance for this formatter
34561      */
34562     getLocale: function() {
34563         return this.locale;
34564     },
34565 
34566     /**
34567      * Return the template string that is used to format date/times for this
34568      * formatter instance. This will work, even when the template property is not explicitly
34569      * given in the options to the constructor. Without the template option, the constructor
34570      * will build the appropriate template according to the options and use that template
34571      * in the format method.
34572      *
34573      * @return {string} the format template for this formatter
34574      */
34575     getTemplate: function() {
34576         return this.template;
34577     },
34578 
34579     /**
34580      * Convert this formatter to a string representation by returning the
34581      * format template. This method delegates to getTemplate.
34582      *
34583      * @return {string} the format template
34584      */
34585     toString: function() {
34586         return this.getTemplate();
34587     },
34588 
34589     /**
34590      * Return whether or not this formatter will auto-scale the units while formatting.
34591      * @returns {boolean} true if auto-scaling is turned on
34592      */
34593     getScale: function() {
34594         return this.scale;
34595     },
34596 
34597     /**
34598      * Return the measurement system that is used for this formatter.
34599      * @returns {string} the measurement system used in this formatter
34600      */
34601     getMeasurementSystem: function() {
34602         return this.measurementSystem;
34603     },
34604 
34605     /**
34606      * @private
34607      */
34608     _format: function(u, system) {
34609         var unit = u.getUnit() === "long-ton" ? "ton" : u.getUnit();
34610         var formatted = new IString(this.template[unit]);
34611         // make sure to use the right plural rules
34612         formatted.setLocale(this.locale, true, undefined, undefined);
34613         var rounded = this.numFmt[system].constrain(u.amount);
34614         formatted = formatted.formatChoice(rounded, {n: this.numFmt[system].format(u.amount)});
34615         return formatted.length > 0 ? formatted : rounded + " " + u.unit;
34616     },
34617 
34618     /**
34619      * Format a particular unit instance according to the settings of this
34620      * formatter object.
34621      *
34622      * @param {Measurement} measurement measurement to format
34623      * @return {string} the formatted version of the given date instance
34624      */
34625     format: function (measurement) {
34626         var u = measurement, system, listStyle;
34627         var doScale = this.scale;
34628 
34629         if (this.convert) {
34630             if (this.measurementSystem) {
34631                 if (this.measurementSystem !== measurement.getMeasurementSystem()) {
34632                     u = u.convertSystem(this.measurementSystem);
34633                 }
34634             } else if (!this.usageInfo || Measurement.getMeasurementSystemForLocale(this.locale) !== u.getMeasurementSystem()) {
34635                 u = measurement.localize(this.locale);
34636             }
34637 
34638             doScale = (this.usageInfo && measurement.getMeasurementSystem() !== u.getMeasurementSystem()) || this.scale;
34639         }
34640 
34641         system = u.getMeasurementSystem() || this.getMeasurementSystem() || "metric";
34642         listStyle = (this.style === "list" || (this.usageInfo && this.usageInfo.systems && this.usageInfo.systems[system].style === "list"));
34643 
34644         if (doScale) {
34645             if (this.usageInfo && measurement.getMeasure() === this.usageInfo.type && !listStyle) {
34646                 // scaling with a restricted set of units
34647                 u = u.scale(system, this.units);
34648             } else {
34649                 u = u.scale(); // scale within the current system
34650             }
34651         }
34652 
34653         if (listStyle) {
34654             var numFmt = this.numFmt[system];
34655             u = u.expand(undefined, this.units, ilib.bind(numFmt, numFmt.constrain), this.scale);
34656             var formatted = u.map(ilib.bind(this, function(unit) {
34657                 return this._format(unit, system);
34658             }));
34659             if (this.listFmt && formatted.length) {
34660                 return this.listFmt.format(formatted);
34661             } else {
34662                 return formatted.join(' ');
34663             }
34664         } else {
34665             return this._format(u, system);
34666         }
34667     }
34668 };
34669 
34670 
34671 /*< Charset.js */
34672 /*
34673  * Charset.js - Return information about a particular character set
34674  * 
34675  * Copyright © 2014-2015, JEDLSoft
34676  *
34677  * Licensed under the Apache License, Version 2.0 (the "License");
34678  * you may not use this file except in compliance with the License.
34679  * You may obtain a copy of the License at
34680  *
34681  *     http://www.apache.org/licenses/LICENSE-2.0
34682  *
34683  * Unless required by applicable law or agreed to in writing, software
34684  * distributed under the License is distributed on an "AS IS" BASIS,
34685  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34686  *
34687  * See the License for the specific language governing permissions and
34688  * limitations under the License.
34689  */
34690 
34691 // !depends ilib.js Utils.js
34692 // !data charsetaliases charset/ISO-8859-1 charset/ISO-8859-15 charset/UTF-8
34693 
34694 
34695 /**
34696  * @class
34697  * Create a new character set info instance. Charset instances give information about
34698  * a particular character set, such as whether or not it is single byte or multibyte,
34699  * and which languages commonly use that charset.<p>
34700  * 
34701  * The optional options object holds extra parameters if they are necessary. The
34702  * current list of supported options are:
34703  * 
34704  * <ul>
34705  * <li><i>name</i> - the name of the charset. This can be given as any commonly
34706  * used name for the character set, which is normalized to a standard IANA name 
34707  * before its info is loaded. If a name is not given,
34708  * this class will return information about the base character set of Javascript,
34709  * which is currently Unicode as encoded in UTF-16.
34710  * 
34711  * <li><i>onLoad</i> - a callback function to call when this object is fully 
34712  * loaded. When the onLoad option is given, this class will attempt to
34713  * load any missing data using the ilib loader callback.
34714  * When the constructor is done (even if the data is already preassembled), the 
34715  * onLoad function is called with the current instance as a parameter, so this
34716  * callback can be used with preassembled or dynamic loading or a mix of the two.
34717  * 
34718  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
34719  * asynchronously. If this option is given as "false", then the "onLoad"
34720  * callback must be given, because the instance returned from this constructor will
34721  * not be usable for a while.
34722  *
34723  * <li><i>loadParams</i> - an object containing parameters to pass to the 
34724  * loader callback function when data is missing. The parameters are not
34725  * interpretted or modified in any way. They are simply passed along. The object 
34726  * may contain any property/value pairs as long as the calling code is in
34727  * agreement with the loader callback function as to what those parameters mean.
34728  * </ul>
34729  * 
34730  * If this copy of ilib is pre-assembled and all the data is already available, 
34731  * or if the data was already previously loaded, then this constructor will call
34732  * the onLoad callback immediately when the initialization is done. 
34733  * If the onLoad option is not given, this class will only attempt to load any
34734  * missing data synchronously.
34735  * 
34736  * Depends directive: !depends charset.js
34737  * 
34738  * @constructor
34739  * @see {ilib.setLoaderCallback} for information about registering a loader callback instance
34740  * @param {Object=} options options which govern the construction of this instance
34741  */
34742 var Charset = function(options) {
34743 	var sync = true,
34744 	    loadParams = undefined;
34745 	this.originalName = "UTF-8";
34746 	
34747 	if (options) {
34748 		if (typeof(options.name) !== 'undefined') {
34749 			this.originalName = options.name;
34750 		}
34751 		
34752 		if (typeof(options.sync) !== 'undefined') {
34753 			sync = !!options.sync;
34754 		}
34755 		
34756 		if (typeof(options.loadParams) !== 'undefined') {
34757 			loadParams = options.loadParams;
34758 		}
34759 	}
34760 
34761 	// default data. A majority of charsets use this info
34762 	this.info = {
34763 		description: "default",
34764 		min: 1,
34765 		max: 1,
34766 		bigendian: true,
34767 		scripts: ["Latn"],
34768 		locales: ["*"]
34769 	};
34770 
34771 	Utils.loadData({
34772 		object: "Charset", 
34773 		locale: "-",
34774 		nonlocale: true,
34775 		name: "charsetaliases.json", 
34776 		sync: sync,
34777 		loadParams: loadParams, 
34778 		callback: ilib.bind(this, function (info) {
34779 			// first map the given original name to one of the standardized IANA names
34780 			if (info) {
34781 				// recognize better by getting rid of extraneous crap and upper-casing
34782 				// it so that the match is case-insensitive
34783 				var n = this.originalName.replace(/[-_,:\+\.\(\)]/g, '').toUpperCase();
34784 				this.name = info[n];
34785 			}
34786 			if (!this.name) {
34787 				this.name = this.originalName;
34788 			}
34789 			Utils.loadData({
34790 				object: "Charset", 
34791 				locale: "-",
34792 				nonlocale: true,
34793 				name: "charset/" + this.name + ".json", 
34794 				sync: sync, 
34795 				loadParams: loadParams, 
34796 				callback: ilib.bind(this, function (info) {
34797 					if (info) {
34798 						ilib.extend(this.info, info);	
34799 					}
34800 					if (options && typeof(options.onLoad) === 'function') {
34801 						options.onLoad(this);
34802 					}
34803 				})
34804 			});
34805 		})
34806 	});
34807 };
34808 
34809 Charset.prototype = {
34810     /**
34811      * Return the standard normalized name of this charset.  The list of standard names 
34812      * comes from the IANA registry of character set names at 
34813      * <a href="http://www.iana.org/assignments/character-sets/character-sets.xhtml">http://www.iana.org/assignments/character-sets/character-sets.xhtml</a>.
34814      * 
34815      * @returns {string} the name of the charset
34816      */
34817     getName: function () {
34818     	return this.name;	
34819     },
34820     
34821     /**
34822      * Return the original name that this instance was constructed with before it was
34823      * normalized to the standard name returned by {@link #getName}.
34824      * 
34825      * @returns {string} the original name that this instance was constructed with
34826      */
34827     getOriginalName: function() {
34828     	return this.originalName;
34829     },
34830     
34831     /**
34832      * Return a short description of the character set.
34833      * 
34834      * @returns {string} a description of the character set
34835      */
34836     getDescription: function() {
34837     	return this.info.description || this.getName();
34838     },
34839     
34840     /**
34841      * Return the smallest number of bytes that a single character in this charset
34842      * could use. For most charsets, this is 1, but for some charsets such as Unicode
34843      * encoded in UTF-16, this may be 2 or more.
34844      * @returns {number} the smallest number of bytes that a single character in
34845      * this charset uses
34846      */
34847     getMinCharWidth: function () {
34848     	return this.info.min;
34849     },
34850     
34851     /**
34852      * Return the largest number of bytes that a single character in this charset
34853      * could use.
34854      * @returns {number} the largest number of bytes that a single character in
34855      * this charset uses
34856      */
34857     getMaxCharWidth: function () {
34858     	return this.info.max;
34859     },
34860     
34861     /**
34862      * Return true if this is a multibyte character set, or false for a fixed
34863      * width character set. A multibyte character set is one in which the characters
34864      * have a variable width. That is, one character may use 1 byte and a different
34865      * character might use 2 or 3 bytes.
34866      * 
34867      * @returns {boolean} true if this is a multibyte charset, or false otherwise
34868      */
34869     isMultibyte: function() {
34870     	return this.getMaxCharWidth() > this.getMinCharWidth();
34871     },
34872     
34873     /**
34874      * Return whether or not characters larger than 1 byte use the big endian order
34875      * or little endian.
34876      * 
34877      * @returns {boolean} true if this character set uses big endian order, or false
34878      * otherwise
34879      */
34880     isBigEndian: function() {
34881     	return this.info.bigendian;
34882     },
34883     
34884     /**
34885      * Return an array of ISO script codes whose characters can be encoded with this 
34886      * character set.
34887      * 
34888      * @returns {Array.<string>} an array of ISO script codes supported by this charset
34889      */
34890     getScripts: function() {
34891     	return this.info.scripts;
34892     }
34893 };
34894 
34895 
34896 /*< Charmap.js */
34897 /*
34898  * Charmap.js - A character set mapping class
34899  * 
34900  * Copyright © 2014-2015,2018, JEDLSoft
34901  *
34902  * Licensed under the Apache License, Version 2.0 (the "License");
34903  * you may not use this file except in compliance with the License.
34904  * You may obtain a copy of the License at
34905  *
34906  *     http://www.apache.org/licenses/LICENSE-2.0
34907  *
34908  * Unless required by applicable law or agreed to in writing, software
34909  * distributed under the License is distributed on an "AS IS" BASIS,
34910  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
34911  *
34912  * See the License for the specific language governing permissions and
34913  * limitations under the License.
34914  */
34915 
34916 // !depends ilib.js JSUtils.js IString.js
34917 
34918 // !data charset/US-ASCII charset/ISO-10646-UCS-2 charset/ISO-10646-UCS-4 charset/ISO-10646-Unicode-Latin1
34919 
34920 
34921 /**
34922  * @class
34923  * Create a new default character set mapping instance. This class is the parent
34924  * class of all of the charmapping subclasses, and only implements basic US-ASCII
34925  * mapping. The subclasses implement all other charsets, some algorithmically, and
34926  * some in a table-based way. Use {@link CharmapFactory} to create the correct
34927  * subclass instance for the desired charmap.<p>
34928  * 
34929  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
34930  * character set and encoding used by Javascript itself. In order to convert 
34931  * between two non-Unicode character sets, you must chain two charmap instances together 
34932  * to first map to Unicode and then back to the second charset. <p>
34933  * 
34934  * The options parameter controls which mapping is constructed and its behaviours. The 
34935  * current list of supported options are:
34936  * 
34937  * <ul>
34938  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
34939  * character. For example, if you are mapping Unicode characters to a particular native
34940  * character set that does not support particular Unicode characters, the mapper will
34941  * follow the behaviour specified in this property. Valid values are:
34942  * <ul>
34943  * <li><i>skip</i> - skip any characters that do not exist in the target charset
34944  * <li><i>placeholder</i> - put a static placeholder character in the output string 
34945  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
34946  * parameter to specify which character to use in this case
34947  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
34948  * </ul>
34949  * The default value for the missing property if not otherwise specified is "escape"
34950  * so that information is not lost.
34951  * 
34952  * <li><i>placeholder</i> - specify the placeholder character to use when the 
34953  * mapper cannot map a particular input character to the output string. If this
34954  * option is not specified, then the '?' (question mark) character is used where 
34955  * possible.
34956  * 
34957  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
34958  * escape unknown characters in the input when mapping to native, and what
34959  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
34960  * values are:
34961  * <ul>
34962  * <li><i>html</i> - Escape the characters as HTML entities. This would use
34963  * the standard HTML 5.0 (or later) entity names where possible, and numeric
34964  * entities in all other cases. Eg. an "e" with an acute accent would be 
34965  * "é"
34966  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
34967  * accent would be "\u00E9". This can also be specified as "c#" as
34968  * it uses a similar escape syntax.
34969  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
34970  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
34971  * acute accent would be "\x00E9". This can also be specified as "c++".
34972  * <li><i>java</i> - Use the Java escape style. This is very similar to the
34973  * the Javascript style, but the backslash has to be escaped twice. Eg. an
34974  * "e" with an acute accent would be "\\u00E9". This can also be specified
34975  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
34976  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
34977  * accent would be "\N{U+00E9}"
34978  * </ul>
34979  * The default if this style is not specified is "js" for Javascript.
34980  * </ul>
34981  * 
34982  * If this copy of ilib is pre-assembled and all the data is already available, 
34983  * or if the data was already previously loaded, then this constructor will call
34984  * the onLoad callback immediately when the initialization is done. 
34985  * If the onLoad option is not given, this class will only attempt to load any
34986  * missing data synchronously.
34987  * 
34988  * @constructor
34989  * @param {Object=} options options which govern the construction of this instance
34990  */
34991 var Charmap = function(options) {
34992 	if (options && options.noinstance) {
34993 	    return;
34994 	}
34995 	
34996 	this.missing = "placeholder";
34997 	this.placeholder = "?";
34998 	this.escapeStyle = "js";
34999 	this.expansionFactor = 1;
35000 	
35001 	if (options) {
35002 		if (typeof(options.placeholder) !== 'undefined') {
35003 			this.placeholder = options.placeholder;
35004 		}
35005 
35006 		var escapes = {
35007 			"html": "html",
35008 			"js": "js",
35009 			"c#": "js",
35010 			"c": "c",
35011 			"c++": "c",
35012 			"java": "java",
35013 			"ruby": "java",
35014 			"perl": "perl"
35015 		};
35016 		
35017 		if (typeof(options.escapeStyle) !== 'undefined') {
35018 			if (typeof(escapes[options.escapeStyle]) !== 'undefined') {
35019 				this.escapeStyle = escapes[options.escapeStyle];
35020 			}
35021 		}
35022 
35023 		if (typeof(options.missing) !== 'undefined') {
35024 			if (options.missing === "skip" || options.missing === "placeholder" || options.missing === "escape") {
35025 				this.missing = options.missing;
35026 			}
35027 		}
35028 	}
35029 };
35030 
35031 /**
35032  * A place for the algorithmic conversions to register themselves as 
35033  * they are defined.
35034  * 
35035  * @static
35036  * @private
35037  */
35038 Charmap._algorithms = {};
35039 
35040 Charmap.prototype = {
35041     /**
35042      * Return the standard name of this charmap. All charmaps map from
35043      * Unicode to the native charset, so the name returned from this
35044      * function corresponds to the native charset.
35045      * 
35046      * @returns {string} the name of the locale's language in English
35047      */
35048     getName: function () {
35049     	return this.charset.getName();	
35050     },
35051     
35052     /**
35053      * @private
35054      */
35055     writeNative: function (array, start, value) {
35056     	// console.log("Charmap.writeNative: start " + start + " adding " + JSON.stringify(value));
35057     	if (ilib.isArray(value)) { 
35058 	    	for (var i = 0; i < value.length; i++) {
35059 	    		array[start+i] = value[i];
35060 	    	}
35061 	    	
35062 	    	return value.length;
35063     	} else {
35064     		array[start] = value;
35065     		return 1;
35066     	}
35067     },
35068     
35069     /**
35070      * @private
35071      */
35072     writeNativeString: function (array, start, string) {
35073     	// console.log("Charmap.writeNativeString: start " + start + " adding " + JSON.stringify(string));
35074     	for (var i = 0; i < string.length; i++) {
35075     		array[start+i] = string.charCodeAt(i);
35076     	}
35077     	return string.length;
35078     },
35079     
35080     /**
35081      * @private
35082      */
35083     _calcExpansionFactor: function() {
35084     	var factor = 1;
35085     	factor = Math.max(factor, this.charset.getMaxCharWidth());
35086     	switch (this.missing) {
35087     	case "placeholder":
35088     		if (this.placeholder) {
35089     			factor = Math.max(factor, this.placeholder.length);
35090     		}
35091     		break;
35092     	case "escape":
35093     		switch (this.escapeStyle) {
35094 			case "html":
35095 				factor = Math.max(factor, 8); // &#xHHHH;
35096 				break;
35097 			case "c":
35098 				factor = Math.max(factor, 6); // \xHHHH
35099 				break;
35100 			case "perl":
35101 				factor = Math.max(factor, 10); // \N{U+HHHH}
35102 				break;
35103 				
35104 			default:
35105 				factor = Math.max(factor, 6); // \uHHHH
35106 				break;
35107     		}
35108     		break;
35109 		default:
35110 			break;
35111     	}
35112     	
35113     	this.expansionFactor = factor;
35114     },
35115     
35116     /**
35117      * @private
35118      */
35119     dealWithMissingChar: function(c) {
35120     	var seq = "";
35121     	
35122 		switch (this.missing) {
35123 			case "skip":
35124 				// do nothing
35125 				break;
35126 				
35127 			case "escape":
35128 				var num = (typeof(c) === 'string') ? c.charCodeAt(0) : c;
35129 				var bigc = JSUtils.pad(num.toString(16), 4).toUpperCase();
35130 				switch (this.escapeStyle) {
35131 					case "html":
35132 						seq = "&#x" + bigc + ";";
35133 						break;
35134 					case "c":
35135 						seq = "\\x" + bigc;
35136 						break;
35137 					case "java":
35138 						seq = "\\\\u" + bigc;
35139 						break;
35140 					case "perl":
35141 						seq = "\\N{U+" + bigc + "}";
35142 						break;
35143 						
35144 					default:
35145 					case "js":
35146 						seq = "\\u" + bigc;
35147 						break;
35148 				}
35149 				break;
35150 				
35151 			default:
35152 			case "placeholder":
35153 				seq = this.placeholder;
35154 				break;
35155 		}
35156 		
35157 		return seq;
35158     },
35159     
35160     /**
35161      * Map a string to the native character set. This string may be 
35162      * given as an intrinsic Javascript string object or an IString 
35163      * object.
35164      * 
35165      * @param {string|IString} string string to map to a different 
35166      * character set. 
35167      * @return {Uint8Array} An array of bytes representing the string 
35168      * in the native character set
35169      */
35170     mapToNative: function(string) {
35171     	if (!string) {
35172     		return new Uint8Array(0);
35173     	}
35174     	
35175     	if (this.algorithm) {
35176     		return this.algorithm.mapToNative(string);
35177     	}
35178     	
35179     	// the default algorithm is plain old ASCII
35180     	var str = (string instanceof IString) ? string : new IString(string);
35181     	
35182     	// use IString's iterator so that we take care of walking through
35183     	// the code points correctly, including the surrogate pairs
35184     	var c, i = 0, it = str.iterator();
35185     	var ret = new Uint8Array(str.length * this.expansionFactor);
35186     	
35187     	while (it.hasNext() && i < ret.length) {
35188     		c = it.next();
35189     		if (c < 127) {
35190     			ret[i++] = c;
35191     		} else {
35192     			i += this.writeNativeString(ret, i, this.dealWithMissingChar(c));
35193     		}
35194     	}
35195 
35196     	return ret;
35197     },
35198     
35199     /**
35200      * Map a native string to the standard Javascript charset of UTF-16. 
35201      * This string may be given as an array of numbers where each number 
35202      * represents a code point in the "from" charset, or as a Uint8Array 
35203      * array of bytes representing the bytes of the string in order.
35204      * 
35205      * @param {Array.<number>|Uint8Array} bytes bytes to map to 
35206      * a Unicode string
35207      * @return {string} A string in the standard Javascript charset UTF-16
35208      */
35209     mapToUnicode: function(bytes) {
35210     	var ret = "";
35211     	var c, i = 0;
35212     	
35213     	while (i < bytes.length) {
35214     		c = bytes[i];
35215     		
35216     		// the default algorithm is plain old ASCII
35217         	if (c < 128) {
35218     			ret += String.fromCharCode(c);
35219     		} else {
35220     			// The byte at "i" wasn't ASCII
35221 				ret += this.dealWithMissingChar(bytes[i++]);
35222     		}
35223     	}
35224 
35225     	return ret;
35226     }
35227 };
35228 
35229 
35230 /*< CharmapTable.js */
35231 /*
35232  * CharmapTable.js - A character set mapping class that maps using trie table
35233  * 
35234  * Copyright © 2014-2015,2018, JEDLSoft
35235  *
35236  * Licensed under the Apache License, Version 2.0 (the "License");
35237  * you may not use this file except in compliance with the License.
35238  * You may obtain a copy of the License at
35239  *
35240  *     http://www.apache.org/licenses/LICENSE-2.0
35241  *
35242  * Unless required by applicable law or agreed to in writing, software
35243  * distributed under the License is distributed on an "AS IS" BASIS,
35244  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35245  *
35246  * See the License for the specific language governing permissions and
35247  * limitations under the License.
35248  */
35249 
35250 // !depends ilib.js Utils.js Charset.js Charmap.js IString.js
35251 
35252 // !data charmaps/ISO-8859-1 charset/ISO-8859-1
35253 
35254 
35255 /**
35256  * @class
35257  * Create a new character set mapping instance using based on a trie table. Charmap 
35258  * instances map strings to 
35259  * other character sets. The charsets can be of any type, single-byte, multi-byte,
35260  * shifting, etc. <p>
35261  * 
35262  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
35263  * character set and encoding used by Javascript itself. In order to convert 
35264  * between two non-Unicode character sets, you must chain two charmap instances together 
35265  * to first map to Unicode and then back to the second charset. <p>
35266  * 
35267  * The options parameter controls which mapping is constructed and its behaviours. The 
35268  * current list of supported options are:
35269  * 
35270  * <ul>
35271  * <li><i>charset</i> - the name of the native charset to map to or from. This can be 
35272  * given as an {@link Charset} instance or as a string that contains any commonly used name 
35273  * for the character set, which is normalized to a standard IANA name. 
35274  * If a name is not given, this class will default to the Western European character 
35275  * set called ISO-8859-15.
35276  * 
35277  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
35278  * character. For example, if you are mapping Unicode characters to a particular native
35279  * character set that does not support particular Unicode characters, the mapper will
35280  * follow the behaviour specified in this property. Valid values are:
35281  * <ul>
35282  * <li><i>skip</i> - skip any characters that do not exist in the target charset
35283  * <li><i>placeholder</i> - put a static placeholder character in the output string 
35284  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
35285  * parameter to specify which character to use in this case
35286  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
35287  * </ul>
35288  * The default value for the missing property if not otherwise specified is "escape"
35289  * so that information is not lost.
35290  * 
35291  * <li><i>placeholder</i> - specify the placeholder character to use when the 
35292  * mapper cannot map a particular input character to the output string. If this
35293  * option is not specified, then the '?' (question mark) character is used where 
35294  * possible.
35295  * 
35296  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
35297  * escape unknown characters in the input when mapping to native, and what
35298  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
35299  * values are:
35300  * <ul>
35301  * <li><i>html</i> - Escape the characters as HTML entities. This would use
35302  * the standard HTML 5.0 (or later) entity names where possible, and numeric
35303  * entities in all other cases. Eg. an "e" with an acute accent would be 
35304  * "é"
35305  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
35306  * accent would be "\u00E9". This can also be specified as "c#" as
35307  * it uses a similar escape syntax.
35308  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
35309  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
35310  * acute accent would be "\x00E9". This can also be specified as "c++".
35311  * <li><i>java</i> - Use the Java escape style. This is very similar to the
35312  * the Javascript style, but the backslash has to be escaped twice. Eg. an
35313  * "e" with an acute accent would be "\\u00E9". This can also be specified
35314  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
35315  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
35316  * accent would be "\N{U+00E9}"
35317  * </ul>
35318  * The default if this style is not specified is "js" for Javascript.
35319  * 
35320  * <li><i>onLoad</i> - a callback function to call when this object is fully 
35321  * loaded. When the onLoad option is given, this class will attempt to
35322  * load any missing data using the ilib loader callback.
35323  * When the constructor is done (even if the data is already preassembled), the 
35324  * onLoad function is called with the current instance as a parameter, so this
35325  * callback can be used with preassembled or dynamic loading or a mix of the two.
35326  * 
35327  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
35328  * asynchronously. If this option is given as "false", then the "onLoad"
35329  * callback must be given, because the instance returned from this constructor will
35330  * not be usable for a while.
35331  *
35332  * <li><i>loadParams</i> - an object containing parameters to pass to the 
35333  * loader callback function when data is missing. The parameters are not
35334  * interpretted or modified in any way. They are simply passed along. The object 
35335  * may contain any property/value pairs as long as the calling code is in
35336  * agreement with the loader callback function as to what those parameters mean.
35337  * </ul>
35338  * 
35339  * If this copy of ilib is pre-assembled and all the data is already available, 
35340  * or if the data was already previously loaded, then this constructor will call
35341  * the onLoad callback immediately when the initialization is done. 
35342  * If the onLoad option is not given, this class will only attempt to load any
35343  * missing data synchronously.
35344  * 
35345  * @constructor
35346  * @see {ilib.setLoaderCallback} for information about registering a loader callback instance
35347  * @extends Charmap
35348  * @param {Object=} options options which govern the construction of this instance
35349  */
35350 var CharmapTable = function(options) {
35351     var sync = true;
35352 
35353     // console.log("CharmapTable: constructor with options: " + JSON.stringify(options));
35354 
35355     this.parent.call(this, options);
35356     this.charsetName = "ISO-8859-15";
35357 
35358     if (options) {
35359         if (typeof(options.charset) === "object") {
35360             this.charset = options.charset;
35361             this.charsetName = this.charset.getName();
35362         } else if (typeof(options.name) !== 'undefined') {
35363             this.charsetName = options.name;
35364         }
35365     } else {
35366         options = {sync: true};
35367     }
35368 
35369     if (!this.charset) {
35370         new Charset({
35371             name: this.charsetName,
35372             sync: sync,
35373             loadParams: options.loadParams,
35374             onLoad: ilib.bind(this, function(cs) {
35375                 this.charset = cs;
35376                 this._init(options);
35377             })
35378         });
35379     } else {
35380         this._init(options);
35381     }
35382 };
35383 
35384 CharmapTable.prototype = new Charmap({noinstance: true});
35385 CharmapTable.prototype.parent = Charmap;
35386 CharmapTable.prototype.constructor = CharmapTable;
35387 
35388 /**
35389  * Initialize the table charmap object
35390  * @private
35391  */
35392 CharmapTable.prototype._init = function(options) {
35393     this._calcExpansionFactor();
35394     
35395     Utils.loadData({
35396         object: "Charmap", 
35397         locale: "-",
35398         nonlocale: true,
35399         name: "charmaps/" + this.charset.getName() + ".json", 
35400         sync: options.sync, 
35401         loadParams: options.loadParams, 
35402         callback: ilib.bind(this, function (mapping) {
35403             var ret = this;
35404             if (!mapping) {
35405                 if (options.sync) {
35406                     throw "No mapping found for " + this.charset.getName();
35407                 } else {
35408                     ret = undefined;
35409                 }
35410             }
35411 
35412             /** @type {{from:Object,to:Object}} */
35413             this.map = mapping;
35414             if (typeof(options.onLoad) === 'function') {
35415                 options.onLoad(ret);
35416             }
35417         })
35418     });
35419 };
35420 
35421 /**
35422  * Walk a trie to find the value for the current position in the given array.
35423  * @private
35424  */
35425 CharmapTable.prototype._trieWalk = function(trie, array, start) {
35426 	function isValue(node) {
35427 		return (typeof(node) === 'string' || typeof(node) === 'number' ||
35428 			(typeof(node) === 'object' && ilib.isArray(node)));
35429 	}
35430 	
35431 	var lastLeaf = undefined,
35432 		i = start,
35433 		trienode = trie;
35434 	
35435 	while (i < array.length) {
35436 		if (typeof(trienode.__leaf) !== 'undefined') {
35437 			lastLeaf = {
35438 				consumed: i - start + 1,
35439 				value: trienode.__leaf
35440 			};
35441 		}
35442 		if (array[i] === 0) {
35443 			// null-terminator, so end the mapping.
35444 			return {
35445 				consumed: 1,
35446 				value: 0
35447 			};
35448 		} else if (typeof(trienode[array[i]]) !== 'undefined') {
35449 			// we have a mapping
35450 			if (isValue(trienode[array[i]])) {
35451 				// it is a leaf node
35452 				return {
35453 					consumed: i - start + 1,
35454 					value: trienode[array[i]]
35455 				};
35456 			} else {
35457 				// it is an intermediate node
35458     			trienode = trienode[array[i++]];
35459     		}
35460 		} else {
35461 			// no mapping for this array element, so return the last known
35462 			// leaf. If none, this will return undefined.
35463 			return lastLeaf;
35464 		}
35465 	}
35466 
35467 	return undefined;
35468 };
35469     
35470 /**
35471  * Map a string to the native character set. This string may be 
35472  * given as an intrinsic Javascript string object or an IString 
35473  * object.
35474  * 
35475  * @param {string|IString} string string to map to a different 
35476  * character set. 
35477  * @return {Uint8Array} An array of bytes representing the string 
35478  * in the native character set
35479  */
35480 CharmapTable.prototype.mapToNative = function(string) {
35481 	if (!string) {
35482 		return new Uint8Array(0);
35483 	}
35484 	
35485 	var str = (string instanceof IString) ? string : new IString(string);
35486 	
35487 	// use IString's iterator so that we take care of walking through
35488 	// the code points correctly, including the surrogate pairs
35489 	// var c, i = 0, it = str.charIterator();
35490 	var ret = new Uint8Array(str.length * this.expansionFactor);
35491 	
35492 	var i = 0, j = 0;
35493 	
35494 	while (i < string.length) {
35495 		var result = this._trieWalk(this.map.from, string, i);
35496 		if (result) {
35497 			if (result.value) {
35498     			i += result.consumed;
35499     			j += this.writeNative(ret, j, result.value);
35500 			} else {
35501 				// null-termination
35502 				i = string.length;
35503 				this.writeNative(ret, j, [result.value]);
35504 			}
35505 		} else {
35506 			// The unicode char at "i" didn't have any mapping, so
35507 			// deal with the missing char
35508 			j += this.writeNativeString(ret, j, this.dealWithMissingChar(string[i++]));
35509 		}
35510 	}
35511 
35512 	return ret.subarray(0, j);
35513 };
35514 
35515 /**
35516  * Map a native string to the standard Javascript charset of UTF-16. 
35517  * This string may be given as an array of numbers where each number 
35518  * represents a code point in the "from" charset, or as a Uint8Array 
35519  * array of bytes representing the bytes of the string in order.
35520  * 
35521  * @param {Array.<number>|Uint8Array} bytes bytes to map to 
35522  * a Unicode string
35523  * @return {string} A string in the standard Javascript charset UTF-16
35524  */
35525 CharmapTable.prototype.mapToUnicode = function(bytes) {
35526 	var ret = "";
35527 	var i = 0;
35528 	
35529 	while (i < bytes.length) {
35530 		var result = this._trieWalk(this.map.to, bytes, i);
35531 		if (result) {
35532 			if (result.value) {
35533     			i += result.consumed;
35534     			if (typeof(result.value) === 'string') {
35535         			ret += result.value;
35536         		} else if (ilib.isArray(result.value)) {
35537         			for (var j = 0; j < result.value.length; j++) {
35538         				ret += result.value[j];
35539         			}
35540         		} // else error in charmap file??
35541 			} else {
35542 				// null-termination
35543 				i = bytes.length;
35544 			}
35545 		} else {
35546 			// The byte at "i" wasn't a lead byte, so start again at the 
35547 			// next byte instead. This may synchronize the rest 
35548 			// of the string.
35549 			ret += this.dealWithMissingChar(bytes[i++]);
35550 		}
35551 	}
35552 
35553 	return ret;
35554 };
35555 
35556 Charmap._algorithms["CharmapTable"] = CharmapTable;
35557 
35558 
35559 /*< CharmapFactory.js */
35560 /*
35561  * CharmapFactory.js - Factory class to create the right subclasses of a charmap for any 
35562  * given chararacter set.
35563  * 
35564  * Copyright © 2015, JEDLSoft
35565  *
35566  * Licensed under the Apache License, Version 2.0 (the "License");
35567  * you may not use this file except in compliance with the License.
35568  * You may obtain a copy of the License at
35569  *
35570  *     http://www.apache.org/licenses/LICENSE-2.0
35571  *
35572  * Unless required by applicable law or agreed to in writing, software
35573  * distributed under the License is distributed on an "AS IS" BASIS,
35574  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35575  *
35576  * See the License for the specific language governing permissions and
35577  * limitations under the License.
35578  */
35579 
35580 /* !depends ilib.js JSUtils.js Charmap.js CharmapTable.js */
35581 // !data charset/ISO-8859-15 charmaps/ISO-8859-15
35582 
35583 
35584 
35585 /**
35586  * Factory method to create a new instance of a character set mapping (charmap) 
35587  * subclass that is appropriate for the requested charset. Charmap instances map strings to 
35588  * other character sets. The charsets can be of any type, single-byte, multi-byte,
35589  * shifting, etc. <p>
35590  * 
35591  * All mappings are done to or from Unicode in the UTF-16 encoding, which is the base
35592  * character set and encoding used by Javascript itself. In order to convert 
35593  * between two non-Unicode character sets, you must chain two charmap instances together 
35594  * to first map to Unicode and then back to the second charset. <p>
35595  * 
35596  * The options parameter controls which mapping is constructed and its behaviours. The 
35597  * current list of supported options are:
35598  * 
35599  * <ul>
35600  * <li><i>name</i> - the name of the native charset to map to or from. This can be 
35601  * given as an {@link Charset} instance or as a string that contains any commonly used name 
35602  * for the character set, which is normalized to a standard IANA name. 
35603  * If a name is not given, this class will default to the Western European character 
35604  * set called ISO-8859-15.
35605  * 
35606  * <li><i>missing</i> - specify what to do if a mapping is missing for a particular
35607  * character. For example, if you are mapping Unicode characters to a particular native
35608  * character set that does not support particular Unicode characters, the mapper will
35609  * follow the behaviour specified in this property. Valid values are:
35610  * <ul>
35611  * <li><i>skip</i> - skip any characters that do not exist in the target charset
35612  * <li><i>placeholder</i> - put a static placeholder character in the output string 
35613  * wherever there is an unknown character in the input string. Use the <i>placeholder</i> 
35614  * parameter to specify which character to use in this case
35615  * <li><i>escape</i> - use an escape sequence to represent the unknown character 
35616  * </ul>
35617  * The default value for the missing property if not otherwise specified is "escape"
35618  * so that information is not lost.
35619  * 
35620  * <li><i>placeholder</i> - specify the placeholder character to use when the 
35621  * mapper cannot map a particular input character to the output string. If this
35622  * option is not specified, then the '?' (question mark) character is used where 
35623  * possible.
35624  * 
35625  * <li><i>escapeStyle</i> - what style of escape sequences should be used to
35626  * escape unknown characters in the input when mapping to native, and what
35627  * style of espcae sequences should be parsed when mapping to Unicode. Valid 
35628  * values are:
35629  * <ul>
35630  * <li><i>html</i> - Escape the characters as HTML entities. This would use
35631  * the standard HTML 5.0 (or later) entity names where possible, and numeric
35632  * entities in all other cases. Eg. an "e" with an acute accent would be 
35633  * "é"
35634  * <li><i>js</i> - Use the Javascript escape style. Eg. an "e" with an acute
35635  * accent would be "\u00E9". This can also be specified as "c#" as
35636  * it uses a similar escape syntax.
35637  * <li><i>c</i> - Use the C/C++ escape style, which is similar to the the
35638  * Javascript style, but uses an "x" in place of the "u". Eg. an "e" with an 
35639  * acute accent would be "\x00E9". This can also be specified as "c++".
35640  * <li><i>java</i> - Use the Java escape style. This is very similar to the
35641  * the Javascript style, but the backslash has to be escaped twice. Eg. an
35642  * "e" with an acute accent would be "\\u00E9". This can also be specified
35643  * as "ruby", as Ruby uses a similar escape syntax with double backslashes.
35644  * <li><i>perl</i> - Use the Perl escape style. Eg. an "e" with an acute
35645  * accent would be "\N{U+00E9}"
35646  * </ul>
35647  * The default if this style is not specified is "js" for Javascript.
35648  * 
35649  * <li><i>onLoad</i> - a callback function to call when this object is fully 
35650  * loaded. When the onLoad option is given, this class will attempt to
35651  * load any missing data using the ilib loader callback.
35652  * When the constructor is done (even if the data is already preassembled), the 
35653  * onLoad function is called with the current instance as a parameter, so this
35654  * callback can be used with preassembled or dynamic loading or a mix of the two.
35655  * 
35656  * <li><i>sync</i> - tell whether to load any missing data synchronously or 
35657  * asynchronously. If this option is given as "false", then the "onLoad"
35658  * callback must be given, because the instance returned from this constructor will
35659  * not be usable for a while.
35660  *
35661  * <li><i>loadParams</i> - an object containing parameters to pass to the 
35662  * loader callback function when data is missing. The parameters are not
35663  * interpretted or modified in any way. They are simply passed along. The object 
35664  * may contain any property/value pairs as long as the calling code is in
35665  * agreement with the loader callback function as to what those parameters mean.
35666  * </ul>
35667  * 
35668  * If this copy of ilib is pre-assembled and all the data is already available, 
35669  * or if the data was already previously loaded, then this constructor will call
35670  * the onLoad callback immediately when the initialization is done. 
35671  * If the onLoad option is not given, this class will only attempt to load any
35672  * missing data synchronously.
35673  * 
35674  * @static
35675  * @param {Object=} options options controlling the construction of this instance, or
35676  * undefined to use the default options
35677  * @return {Charmap|undefined} an instance of a character set mapping class appropriate for
35678  * the requested charset, or undefined if no mapper could be found that supports the
35679  * requested charset
35680  */
35681 var CharmapFactory = function(options) {
35682 	var charsetName = (options && options.name) || "ISO-8859-15";
35683 	var sync = true;
35684 	
35685 	// console.log("CharmapFactory: called with options: " + JSON.stringify(options));
35686 	
35687 	if (options) {
35688 		if (typeof(options.sync) === 'boolean') {
35689 			sync = options.sync;
35690 		}
35691 	} else {
35692 	    options = {sync: true};
35693 	}
35694 
35695 	var instance;
35696 	
35697 	new Charset({
35698 		name: charsetName,
35699 		sync: sync,
35700 		loadParams: options.loadParams,
35701 		onLoad: function (charset) {
35702 			// name will be normalized already
35703 			var cons, name = charset.getName();
35704 	
35705 			// console.log("CharmapFactory: normalized charset name: " + name);
35706 			
35707 			if (!Charmap._algorithms[name] && ilib.isDynCode()) {
35708 				// console.log("CharmapFactory: isDynCode. Doing require");
35709 				var entry = CharmapFactory._dynMap[name] || "CharmapTable";
35710 				cons = Charmap._algorithms[name] = require("./" + entry + ".js");
35711 			}
35712 			
35713 			if (!cons) {
35714 				cons = Charmap._algorithms[name] || Charmap._algorithms["CharmapTable"];
35715 			}
35716 			
35717 			// console.log("CharmapFactory: cons is "); console.dir(cons);
35718 			
35719 			// pass the same options through to the constructor so the subclass
35720 			// has the ability to do something with if it needs to
35721 			instance = cons && new cons(JSUtils.merge(options || {}, {charset: charset}));
35722 		}
35723 	});
35724 	
35725 	return instance;
35726 };
35727 
35728 
35729 /**
35730  * Map standardized charset names to classes to initialize in the dynamic code model.
35731  * These classes implement algorithmic mappings instead of table-based ones.
35732  * TODO: Need to figure out some way that this doesn't have to be updated by hand.
35733  * @private
35734  */
35735 CharmapFactory._dynMap = {
35736 	"UTF-8":      "UTF8",
35737 	"UTF-16":     "UTF16LE",
35738 	"UTF-16LE":   "UTF16LE",
35739 	"UTF-16BE":   "UTF16BE",
35740 	"US-ASCII":   "Charmap"
35741 	/*
35742 	not implemented yet
35743 	"ISO-2022-JP": "ISO2022",
35744 	"ISO-2022-JP-1": "ISO2022",
35745 	"ISO-2022-JP-2": "ISO2022",
35746 	"ISO-2022-JP-3": "ISO2022",
35747 	"ISO-2022-JP-2004": "ISO2022",
35748 	"ISO-2022-CN": "ISO2022",
35749 	"ISO-2022-CN-EXT": "ISO2022",
35750 	"ISO-2022-KR": "ISO2022"
35751 	*/
35752 };
35753 
35754 
35755 /*< UTF8.js */
35756 /*
35757  * UTF8.js - Implement Unicode Transformation Format 8-bit mappings
35758  * 
35759  * Copyright © 2014-2015, 2018, JEDLSoft
35760  *
35761  * Licensed under the Apache License, Version 2.0 (the "License");
35762  * you may not use this file except in compliance with the License.
35763  * You may obtain a copy of the License at
35764  *
35765  *     http://www.apache.org/licenses/LICENSE-2.0
35766  *
35767  * Unless required by applicable law or agreed to in writing, software
35768  * distributed under the License is distributed on an "AS IS" BASIS,
35769  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35770  *
35771  * See the License for the specific language governing permissions and
35772  * limitations under the License.
35773  */
35774 
35775 // !depends Charmap.js IString.js
35776 
35777 // !data charset/UTF-8
35778 
35779 
35780 /**
35781  * @class
35782  * Create a new UTF-8 mapping instance
35783  * @constructor
35784  * @extends Charmap
35785  */
35786 var UTF8 = function (options) {
35787     options = options || {sync: true};
35788     if (typeof(options.charset) === "object" && options.charset instanceof Charset) {
35789         this.charset = options.charset;
35790         this._init(options);
35791     } else {
35792         new Charset({
35793             name: "UTF-8",
35794             sync: options.sync,
35795             loadParams: options.loadParams,
35796             onLoad: ilib.bind(this, function(cs) {
35797                 this.charset = cs;
35798                 this._init(options);
35799             })
35800         });
35801     }
35802 };
35803 
35804 UTF8.prototype = new Charmap({noinstance: true});
35805 UTF8.prototype.parent = Charmap;
35806 UTF8.prototype.constructor = UTF8;
35807 
35808 /**
35809  * @private
35810  * Initialize the charmap instance
35811  */
35812 UTF8.prototype._init = function(options) {
35813     this._calcExpansionFactor();
35814 
35815     if (typeof(options.onLoad) === "function") {
35816         options.onLoad(this);
35817     }
35818 };
35819 
35820 UTF8.prototype.validate = function(bytes) {
35821 	var i = 0;
35822 	while (i < bytes.length) {
35823 		if ((bytes[i] & 0x80) === 0) {
35824 			i++;
35825 		} else {
35826 			var len;
35827 			if ((bytes[i] & 0xC0) === 0xC0) {
35828 				len = 2;
35829 			} else if ((bytes[i] & 0xE0) === 0xE0) {
35830 				len = 3;
35831 			} else if ((bytes[i] & 0xF0) === 0xF0) {
35832 				len = 4;
35833 			} else {
35834 				// invalid lead byte
35835 				return false;
35836 			}
35837 			if (i + len > bytes.length) {
35838 				// not enough trailing bytes
35839 				return false;
35840 			}
35841 			for (var j = 1; j < len; j++) {
35842 				// check each trailing byte to see if it has the correct form
35843 				if ((bytes[i+j] & 0x80) !== 0x80) {
35844 					return false;
35845 				}
35846 			}
35847 			i += len;
35848 		}
35849 	}
35850 	
35851 	return true;
35852 };
35853 	
35854 UTF8.prototype.mapToUnicode = function (bytes) {
35855 	if (typeof(Buffer) !== "undefined") {
35856 		// nodejs can convert it quickly in native code
35857 		var b = new Buffer(bytes);
35858 		return b.toString("utf8");
35859 	}
35860 	// otherwise we have to implement it in pure JS
35861 	var ret = "";
35862 	var i = 0;
35863 	while (i < bytes.length) {
35864 		if (bytes[i] === 0) {
35865 			// null-terminator
35866 			i = bytes.length;
35867 		} else if ((bytes[i] & 0x80) === 0) {
35868 			// 1 byte char
35869 			ret += String.fromCharCode(bytes[i++]);
35870 		} else if ((bytes[i] & 0xE0) === 0xC0) {
35871 			// 2 byte char
35872 			if (i + 1 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80) {
35873 				throw "invalid utf-8 bytes";
35874 			}
35875 			// xxx xxyyyyyy
35876 			ret += String.fromCharCode((bytes[i] & 0x1F) << 6 | (bytes[i+1] & 0x3F));
35877 			i += 2;
35878 		} else if ((bytes[i] & 0xF0) === 0xE0) {
35879 			// 3 byte char
35880 			if (i + 2 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80 || (bytes[i+2] & 0x80) !== 0x80) {
35881 				throw "invalid utf-8 bytes";
35882 			}
35883 			// xxxxyyyy yyzzzzzz
35884 			ret += String.fromCharCode((bytes[i] & 0xF) << 12 | (bytes[i+1] & 0x3F) << 6 | (bytes[i+2] & 0x3F));
35885 			i += 3;
35886 		} else if ((bytes[i] & 0xF8) === 0xF0) {
35887 			// 4 byte char
35888 			if (i + 3 >= bytes.length || (bytes[i+1] & 0x80) !== 0x80 || (bytes[i+2] & 0x80) !== 0x80 || (bytes[i+3] & 0x80) !== 0x80) {
35889 				throw "invalid utf-8 bytes";
35890 			}
35891 			// wwwxx xxxxyyyy yyzzzzzz
35892 			ret += IString.fromCodePoint((bytes[i] & 0x7) << 18 | (bytes[i+1] & 0x3F) << 12 | (bytes[i+2] & 0x3F) << 6 | (bytes[i+3] & 0x3F));
35893 			i += 4;
35894 		} else {
35895 			throw "invalid utf-8 bytes";
35896 		}
35897 	}
35898 	
35899 	return ret;
35900 };
35901 	
35902 UTF8.prototype.mapToNative = function(str) {
35903 	if (typeof(Buffer) !== "undefined") {
35904 		// nodejs can convert it quickly in native code
35905 		var b = new Buffer(str, "utf8");
35906 		return new Uint8Array(b);
35907 	}
35908 	// otherwise we have to implement it in pure JS
35909 	var istr = (str instanceof IString) ? str : new IString(str);
35910 	
35911 	// step through the surrogate pairs as single code points by using
35912 	// IString's iterator 
35913 	var it = istr.iterator();
35914 	
35915 	// multiply by 4 because the max size of a UTF-8 char is 4 bytes, so
35916 	// this will at least get us enough room to encode everything. Add 1
35917 	// for the null terminator
35918 	var ret = new Uint8Array(istr.length * 4 + 1);
35919 	var i = 0;
35920 	
35921 	while (it.hasNext()) {
35922 		var c = it.next();
35923 		if (c > 0x7F) {
35924 			if (c > 0x7FF) {
35925 				if (c > 0xFFFF) {
35926 					// astral planes char
35927 					ret[i]   = 0xF0 | ((c >> 18) & 0x3);
35928 					ret[i+1] = 0x80 | ((c >> 12) & 0x3F);
35929 					ret[i+2] = 0x80 | ((c >> 6) & 0x3F);
35930 					ret[i+3] = 0x80 | (c & 0x3F);
35931 					
35932 					i += 4;
35933 				} else {
35934 					ret[i]   = 0xE0 | ((c >> 12) & 0xF);
35935 					ret[i+1] = 0x80 | ((c >> 6) & 0x3F);
35936 					ret[i+2] = 0x80 | (c & 0x3F);
35937 					
35938 					i += 3;
35939 				}
35940 			} else {
35941 				ret[i]   = 0xC0 | ((c >> 6) & 0x1F);
35942 				ret[i+1] = 0x80 | (c & 0x3F);
35943 				
35944 				i += 2;
35945 			}
35946 		} else {
35947 			ret[i++] = (c & 0x7F);
35948 		}
35949 	}
35950 	ret[i] = 0; // null-terminate it
35951 	
35952 	return ret;
35953 };
35954 
35955 Charmap._algorithms["UTF-8"] = UTF8;
35956 
35957 
35958 /*< UTF16BE.js */
35959 /*
35960  * UTF16BE.js - Implement Unicode Transformation Format 16-bit,
35961  * Big Endian mappings
35962  * 
35963  * Copyright © 2014-2015, 2018, JEDLSoft
35964  *
35965  * Licensed under the Apache License, Version 2.0 (the "License");
35966  * you may not use this file except in compliance with the License.
35967  * You may obtain a copy of the License at
35968  *
35969  *     http://www.apache.org/licenses/LICENSE-2.0
35970  *
35971  * Unless required by applicable law or agreed to in writing, software
35972  * distributed under the License is distributed on an "AS IS" BASIS,
35973  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35974  *
35975  * See the License for the specific language governing permissions and
35976  * limitations under the License.
35977  */
35978 
35979 // !depends Charmap.js
35980 
35981 // !data charset/UTF-16 charset/UTF-16BE
35982 
35983 
35984 /**
35985  * @class
35986  * Create a new UTF-16BE mapping instance
35987  * @constructor
35988  * @extends Charmap
35989  */
35990 var UTF16BE = function (options) {
35991     options = options || {sync: true};
35992     if (typeof(options.charset) === "object" && options.charset instanceof Charset) {
35993         this.charset = options.charset;
35994         this._init(options);
35995     } else {
35996         new Charset({
35997             name: "UTF-16BE",
35998             sync: options.sync,
35999             loadParams: options.loadParams,
36000             onLoad: ilib.bind(this, function(cs) {
36001                 this.charset = cs;
36002                 this._init(options);
36003             })
36004         });
36005     }
36006 };
36007 
36008 UTF16BE.prototype = new Charmap({noinstance: true});
36009 UTF16BE.prototype.parent = Charmap;
36010 UTF16BE.prototype.constructor = UTF16BE;
36011 
36012 /**
36013  * @private
36014  * Initialize the charmap instance
36015  */
36016 UTF16BE.prototype._init = function(options) {
36017     this._calcExpansionFactor();
36018 
36019     if (typeof(options.onLoad) === "function") {
36020         options.onLoad(this);
36021     }
36022 };
36023 
36024 UTF16BE.prototype.mapToUnicode = function (bytes) {
36025 	// nodejs can't convert big-endian in native code,
36026 	// so we would have to flip each Uint16 ourselves.
36027 	// At that point, it's just quicker to convert 
36028 	// in JS code anyways
36029 	var ret = "";
36030 	for (var i = 0; i < bytes.length; i += 2) {
36031 		ret += String.fromCharCode(bytes[i] << 8 | bytes[i+1]);
36032 	}
36033 	
36034 	return ret;
36035 };
36036 	
36037 UTF16BE.prototype.mapToNative = function(str) {
36038 	// nodejs can't convert big-endian in native code,
36039 	// so we would have to flip each Uint16 ourselves.
36040 	// At that point, it's just quicker to convert 
36041 	// in JS code anyways
36042 	var ret = new Uint8Array(str.length * 2 + 2);
36043 	var c;
36044 	for (var i = 0; i < str.length; i++) {
36045 		c = str.charCodeAt(i);
36046 		ret[i*2] = (c >> 8) & 0xFF;
36047 		ret[i*2+1] = c & 0xFF;
36048 	}
36049 	// double null terminate it, just in case
36050 	ret[i*2+1] = 0;
36051 	ret[i*2+2] = 0;
36052 	
36053 	return ret;
36054 };
36055 
36056 Charmap._algorithms["UTF-16BE"] = UTF16BE;
36057 
36058 
36059 /*< UTF16LE.js */
36060 /*
36061  * UTF16LE.js - Implement Unicode Transformation Format 16 bit, 
36062  * Little Endian mappings
36063  * 
36064  * Copyright © 2014-2015, 2018, JEDLSoft
36065  *
36066  * Licensed under the Apache License, Version 2.0 (the "License");
36067  * you may not use this file except in compliance with the License.
36068  * You may obtain a copy of the License at
36069  *
36070  *     http://www.apache.org/licenses/LICENSE-2.0
36071  *
36072  * Unless required by applicable law or agreed to in writing, software
36073  * distributed under the License is distributed on an "AS IS" BASIS,
36074  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36075  *
36076  * See the License for the specific language governing permissions and
36077  * limitations under the License.
36078  */
36079 
36080 // !depends Charmap.js
36081 
36082 // !data charset/UTF-16 charset/UTF-16LE
36083 
36084 
36085 /**
36086  * @class
36087  * Create a new UTF-16LE mapping instance
36088  * @constructor
36089  * @extends Charmap
36090  */
36091 var UTF16LE = function (options) {
36092     options = options || {sync: true};
36093     if (typeof(options.charset) === "object" && options.charset instanceof Charset) {
36094         this.charset = options.charset;
36095         this._init(options);
36096     } else {
36097         new Charset({
36098             name: "UTF-16LE",
36099             sync: options.sync,
36100             loadParams: options.loadParams,
36101             onLoad: ilib.bind(this, function(cs) {
36102                 this.charset = cs;
36103                 this._init(options);
36104             })
36105         });
36106     }
36107 };
36108 
36109 UTF16LE.prototype = new Charmap({noinstance: true});
36110 UTF16LE.prototype.parent = Charmap;
36111 UTF16LE.prototype.constructor = UTF16LE;
36112 
36113 /**
36114  * @private
36115  * Initialize the charmap instance
36116  */
36117 UTF16LE.prototype._init = function(options) {
36118     this._calcExpansionFactor();
36119 
36120     if (typeof(options.onLoad) === "function") {
36121         options.onLoad(this);
36122     }
36123 };
36124 
36125 UTF16LE.prototype.mapToUnicode = function (bytes) {
36126 	if (typeof(Buffer) !== "undefined") {
36127 		// nodejs can convert it quickly in native code
36128 		var b = new Buffer(bytes);
36129 		return b.toString("utf16le");
36130 	}
36131 	// otherwise we have to implement it in pure JS
36132 	var ret = "";
36133 	for (var i = 0; i < bytes.length; i += 2) {
36134 		ret += String.fromCharCode(bytes[i+1] << 8 | bytes[i]);
36135 	}
36136 	
36137 	return ret;
36138 };
36139 	
36140 UTF16LE.prototype.mapToNative =  function(str) {
36141 	if (typeof(Buffer) !== "undefined") {
36142 		// nodejs can convert it quickly in native code
36143 		var b = new Buffer(str, "utf16le");
36144 		return new Uint8Array(b);
36145 	}
36146 	// otherwise we have to implement it in pure JS
36147 	var ret = new Uint8Array(str.length * 2 + 2);
36148 	var c;
36149 	for (var i = 0; i < str.length; i++) {
36150 		c = str.charCodeAt(i);
36151 		ret[i*2] = c & 0xFF;
36152 		ret[i*2+1] = (c >> 8) & 0xFF;
36153 	}
36154 	// double null terminate it, just in case
36155 	ret[i*2+1] = 0;
36156 	ret[i*2+2] = 0;
36157 	
36158 	return ret;
36159 };
36160 
36161 Charmap._algorithms["UTF-16"] = UTF16LE;
36162 Charmap._algorithms["UTF-16LE"] = UTF16LE;
36163 
36164 
36165 /*< Country.js */
36166 /*
36167  * Country.js - Country class to get country name corresponding to country code in locale assigned
36168  *
36169  * Copyright © 2017, LGE
36170  *
36171  * Licensed under the Apache License, Version 2.0 (the "License");
36172  * you may not use this file except in compliance with the License.
36173  * You may obtain a copy of the License at
36174  *
36175  *     http://www.apache.org/licenses/LICENSE-2.0
36176  *
36177  * Unless required by applicable law or agreed to in writing, software
36178  * distributed under the License is distributed on an "AS IS" BASIS,
36179  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36180  *
36181  * See the License for the specific language governing permissions and
36182  * limitations under the License.
36183  */
36184 
36185 // !depends ilib.js Utils.js Locale.js LocaleInfo.js ResBundle.js
36186 
36187 // !data ctryreverse
36188 
36189 
36190 /**
36191  * @class
36192  * Create a new country information instance. Instances of this class encode
36193  * information about country name.<p>
36194  *
36195  * The options can contain any of the following properties:
36196  *
36197  * <ul>
36198  * <li><i>locale</i> - specify the locale for this instance. Country names are provided
36199  * in the language of this locale.
36200  *
36201  * <li><i>onLoad</i> - a callback function to call when the country name data is fully
36202  * loaded. When the onLoad option is given, this class will attempt to
36203  * load any missing locale data using the ilib loader callback.
36204  * When the constructor is done (even if the data is already preassembled), the
36205  * onLoad function is called with the current instance as a parameter, so this
36206  * callback can be used with preassembled or dynamic loading or a mix of the two.
36207  *
36208  * <li><i>sync</i> - tell whether to load any missing locale data synchronously or
36209  * asynchronously. If this option is given as "false", then the "onLoad"
36210  * callback must be given, as the instance returned from this constructor will
36211  * not be usable for a while.
36212  *
36213  * <li><i>loadParams</i> - an object containing parameters to pass to the
36214  * loader callback function when locale data is missing. The parameters are not
36215  * interpretted or modified in any way. They are simply passed along. The object
36216  * may contain any property/value pairs as long as the calling code is in
36217  * agreement with the loader callback function as to what those parameters mean.
36218  * </ul>
36219  *
36220  * If the locale is not set, the default locale(en-US) will be used.<p>
36221  *
36222  * @constructor
36223  * @param options {Object} a set of properties to govern how this instance is constructed.
36224  */
36225 var Country = function (options) {
36226 	var sync = true,
36227 	    loadParams = undefined,
36228 	    locale;
36229 
36230 	if (options) {
36231 		if (options.locale) {
36232 			this.locale = (typeof(options.locale) === 'string') ? new Locale(options.locale) : options.locale;
36233 		}
36234 		if (typeof(options.sync) !== 'undefined') {
36235 			sync = !!options.sync;
36236 		}
36237 		if (options.loadParams) {
36238 			loadParams = options.loadParams;
36239 		}
36240 	}
36241 
36242 	this.locale = this.locale || new Locale();
36243 	new LocaleInfo(this.locale, {
36244 		sync: sync,
36245 		loadParams: loadParams,
36246 		onLoad: ilib.bind(this, function (li) {
36247 			this.locinfo = li;
36248 			if (this.locinfo.getRegionName() === undefined) {
36249 				locale = 'en-US';
36250 			} else {
36251 				locale = this.locale;
36252 			}
36253 
36254 			if (!this.codeToCountry) {
36255 				Utils.loadData({
36256 					name: "ctryreverse.json",
36257 					object: Country,
36258 					locale: locale,
36259 					sync: sync,
36260 					loadParams: loadParams,
36261 					callback: ilib.bind(this, function(countries) {
36262 						this.codeToCountry = countries;
36263 						this._calculateCountryToCode();
36264 						if (options && typeof(options.onLoad) === 'function') {
36265 							options.onLoad(this);
36266 						}
36267 					})
36268 				});
36269 			} else {
36270 				this._calculateCountryToCode();
36271 				if (options && typeof(options.onLoad) === 'function') {
36272 					options.onLoad(this);
36273 				}
36274 			}
36275 		})
36276 	});
36277 };
36278 
36279 /**
36280  * Return an array of the ids for all ISO 3166-1 alpha-2 code that
36281  * this copy of ilib knows about.
36282  *
36283  * @static
36284  * @return {Object} an object of country code that this copy of ilib knows about.
36285  */
36286 Country.getAvailableCode = function() {
36287 	var countries = new ResBundle({
36288 			name: "ctryreverse"
36289 		}).getResObj();
36290 
36291 	return countries && Object.keys(countries);
36292 };
36293 
36294 /**
36295  * Return an array of country names that this copy of ilib knows about.
36296  *
36297  * @static
36298  * @return {Object} an object of country code that this copy of ilib knows about.
36299  */
36300 Country.getAvailableCountry = function() {
36301 	var ret = [],
36302 		code,
36303 		countries = new ResBundle({
36304 			name: "ctryreverse"
36305 		}).getResObj();
36306 
36307 	for (code in countries) {
36308 		if (code && countries[code]) {
36309 			ret.push(countries[code]);
36310 		}
36311 	}
36312 
36313 	return ret;
36314 };
36315 
36316 Country.prototype = {
36317 	/**
36318 	 * @private
36319 	 */
36320 	_calculateCountryToCode: function() {
36321 		var temp = this.codeToCountry,
36322 				code;
36323 
36324 		this.countryToCode = {};
36325 
36326 		for (code in temp) {
36327 			if (code && temp[code]) {
36328 				this.countryToCode[temp[code]] = code;
36329 			}
36330 		}
36331 	},
36332 
36333 	/**
36334 	 * Return the country code corresponding to the country name given.
36335 	 * If the country name is given, but it is not found in the list of known countries, this
36336 	 * method will throw an exception.
36337 	 * @param {string} ctryname The country name in the language of the locale of this instance
36338 	 * @return {string} the country code corresponding to the country name
36339 	 * @throws "Country xx is unknown" when the given country name is not in the list of
36340 	 * known country names. xx is replaced with the requested country name.
36341 	 */
36342 	getCode: function (ctryname) {
36343 		if (!this.countryToCode[ctryname]) {
36344 			throw "Country " + ctryname + " is unknown";
36345 		}
36346 		return this.countryToCode[ctryname];
36347 	},
36348 
36349 	/**
36350 	 * Return the country name corresponding to the country code given.
36351 	 * If the code is given, but it is not found in the list of known countries, this
36352 	 * method will throw an exception.
36353 	 * @param {string} code The country code to get the country name
36354 	 * @return {string} the country name in the language of the locale of this instance
36355 	 * @throws "Country xx is unknown" when the given country code is not in the list of
36356 	 * known country codes. xx is replaced with the requested country code.
36357 	 */
36358 	getName: function (code) {
36359 		if (!this.codeToCountry[code]) {
36360 			throw "Country " + code + " is unknown";
36361 		}
36362 		return this.codeToCountry[code];
36363 	},
36364 
36365 	/**
36366 	 * Return the locale for this country. If the options to the constructor
36367 	 * included a locale property in order to find the country that is appropriate
36368 	 * for that locale, then the locale is returned here. If the options did not
36369 	 * include a locale, then this method returns undefined.
36370 	 * @return {Locale} the locale used in the constructor of this instance,
36371 	 * or undefined if no locale was given in the constructor
36372 	 */
36373 	getLocale: function () {
36374 		return this.locale;
36375 	}
36376 };
36377 
36378 
36379 
36380 /*< /data/root/home/edwin/src/ilib/js/lib/ilib-full-inc.js */
36381 /**
36382  * @license
36383  * Copyright © 2012-2015, JEDLSoft
36384  *
36385  * Licensed under the Apache License, Version 2.0 (the "License");
36386  * you may not use this file except in compliance with the License.
36387  * You may obtain a copy of the License at
36388  *
36389  *     http://www.apache.org/licenses/LICENSE-2.0
36390  *
36391  * Unless required by applicable law or agreed to in writing, software
36392  * distributed under the License is distributed on an "AS IS" BASIS,
36393  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36394  *
36395  * See the License for the specific language governing permissions and
36396  * limitations under the License.
36397  */
36398 
36399 /*
36400  * ilib-full-inc.js - metafile that includes all other js files
36401  */
36402 
36403 /* !depends
36404 ilib.js
36405 DateRngFmt.js
36406 IDate.js
36407 DateFactory.js
36408 HebrewDate.js
36409 HebrewCal.js
36410 IslamicCal.js
36411 IslamicDate.js
36412 JulianCal.js
36413 JulianDate.js
36414 GregorianCal.js
36415 GregorianDate.js
36416 ThaiSolarCal.js
36417 ThaiSolarDate.js
36418 PersianCal.js
36419 PersianDate.js
36420 PersianAlgoCal.js
36421 PersianAlgoDate.js
36422 HanCal.js
36423 HanDate.js
36424 EthiopicCal.js
36425 EthiopicDate.js
36426 CopticCal.js
36427 CopticDate.js
36428 INumber.js
36429 NumFmt.js
36430 JulianDay.js
36431 DateFmt.js
36432 Calendar.js
36433 CalendarFactory.js
36434 Utils.js
36435 Locale.js
36436 IString.js
36437 DurationFmt.js
36438 ResBundle.js
36439 CType.js
36440 LocaleInfo.js
36441 DateRngFmt.js
36442 isAlnum.js
36443 isAlpha.js
36444 isAscii.js
36445 isBlank.js
36446 isCntrl.js
36447 isDigit.js
36448 isGraph.js
36449 isIdeo.js
36450 isLower.js
36451 isPrint.js
36452 isPunct.js
36453 isSpace.js
36454 isUpper.js
36455 isXdigit.js
36456 isScript.js
36457 ScriptInfo.js
36458 Name.js
36459 NameFmt.js
36460 Address.js
36461 AddressFmt.js
36462 Collator.js
36463 nfkc/all.js
36464 LocaleMatcher.js
36465 NormString.js
36466 CaseMapper.js
36467 GlyphString.js
36468 PhoneFmt.js
36469 PhoneGeoLocator.js
36470 PhoneNumber.js
36471 Measurement.js
36472 MeasurementFactory.js
36473 UnitFmt.js
36474 LengthUnit.js
36475 VelocityUnit.js
36476 DigitalStorageUnit.js
36477 TemperatureUnit.js
36478 UnknownUnit.js
36479 TimeUnit.js
36480 MassUnit.js
36481 AreaUnit.js
36482 FuelConsumptionUnit.js
36483 VolumeUnit.js	
36484 EnergyUnit.js
36485 Charset.js
36486 Charmap.js
36487 CharmapFactory.js
36488 CharmapTable.js
36489 UTF8.js
36490 UTF16BE.js
36491 UTF16LE.js
36492 Country.js
36493 ListFmt.js
36494 DigitalSpeedUnit.js
36495 */
36496 
36497